pulse_meter_visualizer 0.4.11

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 (124) hide show
  1. data/.gitignore +19 -0
  2. data/.rbenv-version +1 -0
  3. data/.rspec +1 -0
  4. data/.rvmrc +1 -0
  5. data/.travis.yml +8 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +22 -0
  8. data/Procfile +3 -0
  9. data/README.md +40 -0
  10. data/Rakefile +72 -0
  11. data/examples/basic.ru +145 -0
  12. data/examples/basic_sensor_data.rb +96 -0
  13. data/lib/pulse_meter/visualize/app.rb +78 -0
  14. data/lib/pulse_meter/visualize/base.rb +15 -0
  15. data/lib/pulse_meter/visualize/coffee/application.coffee +40 -0
  16. data/lib/pulse_meter/visualize/coffee/collections/page_info_list.coffee +17 -0
  17. data/lib/pulse_meter/visualize/coffee/collections/sensor_info_list.coffee +4 -0
  18. data/lib/pulse_meter/visualize/coffee/collections/widget_list.coffee +14 -0
  19. data/lib/pulse_meter/visualize/coffee/extensions.coffee +26 -0
  20. data/lib/pulse_meter/visualize/coffee/models/dinamic_widget.coffee +34 -0
  21. data/lib/pulse_meter/visualize/coffee/models/page_info.coffee +2 -0
  22. data/lib/pulse_meter/visualize/coffee/models/sensor_info.coffee +2 -0
  23. data/lib/pulse_meter/visualize/coffee/models/widget.coffee +54 -0
  24. data/lib/pulse_meter/visualize/coffee/presenters/area.coffee +2 -0
  25. data/lib/pulse_meter/visualize/coffee/presenters/gauge.coffee +11 -0
  26. data/lib/pulse_meter/visualize/coffee/presenters/line.coffee +2 -0
  27. data/lib/pulse_meter/visualize/coffee/presenters/pie.coffee +20 -0
  28. data/lib/pulse_meter/visualize/coffee/presenters/series.coffee +44 -0
  29. data/lib/pulse_meter/visualize/coffee/presenters/table.coffee +10 -0
  30. data/lib/pulse_meter/visualize/coffee/presenters/timeline.coffee +13 -0
  31. data/lib/pulse_meter/visualize/coffee/presenters/widget.coffee +65 -0
  32. data/lib/pulse_meter/visualize/coffee/router.coffee +21 -0
  33. data/lib/pulse_meter/visualize/coffee/views/dynamic_chart.coffee +91 -0
  34. data/lib/pulse_meter/visualize/coffee/views/dynamic_widget.coffee +58 -0
  35. data/lib/pulse_meter/visualize/coffee/views/page_title.coffee +17 -0
  36. data/lib/pulse_meter/visualize/coffee/views/page_titles.coffee +15 -0
  37. data/lib/pulse_meter/visualize/coffee/views/sensor_info_list.coffee +19 -0
  38. data/lib/pulse_meter/visualize/coffee/views/widget.coffee +99 -0
  39. data/lib/pulse_meter/visualize/coffee/views/widget_chart.coffee +13 -0
  40. data/lib/pulse_meter/visualize/coffee/views/widget_list.coffee +15 -0
  41. data/lib/pulse_meter/visualize/dsl/base.rb +131 -0
  42. data/lib/pulse_meter/visualize/dsl/errors.rb +40 -0
  43. data/lib/pulse_meter/visualize/dsl/layout.rb +27 -0
  44. data/lib/pulse_meter/visualize/dsl/page.rb +33 -0
  45. data/lib/pulse_meter/visualize/dsl/sensor.rb +20 -0
  46. data/lib/pulse_meter/visualize/dsl/widget.rb +37 -0
  47. data/lib/pulse_meter/visualize/dsl/widgets/area.rb +20 -0
  48. data/lib/pulse_meter/visualize/dsl/widgets/gauge.rb +12 -0
  49. data/lib/pulse_meter/visualize/dsl/widgets/line.rb +21 -0
  50. data/lib/pulse_meter/visualize/dsl/widgets/pie.rb +16 -0
  51. data/lib/pulse_meter/visualize/dsl/widgets/table.rb +19 -0
  52. data/lib/pulse_meter/visualize/layout.rb +79 -0
  53. data/lib/pulse_meter/visualize/page.rb +25 -0
  54. data/lib/pulse_meter/visualize/public/css/application.css +56 -0
  55. data/lib/pulse_meter/visualize/public/css/bootstrap.css +4883 -0
  56. data/lib/pulse_meter/visualize/public/css/bootstrap.min.css +729 -0
  57. data/lib/pulse_meter/visualize/public/css/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  58. data/lib/pulse_meter/visualize/public/css/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  59. data/lib/pulse_meter/visualize/public/css/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  60. data/lib/pulse_meter/visualize/public/css/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  61. data/lib/pulse_meter/visualize/public/css/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  62. data/lib/pulse_meter/visualize/public/css/images/ui-bg_glass_75_ffffff_1x400.png +0 -0
  63. data/lib/pulse_meter/visualize/public/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  64. data/lib/pulse_meter/visualize/public/css/images/ui-bg_inset-soft_95_fef1ec_1x100.png +0 -0
  65. data/lib/pulse_meter/visualize/public/css/images/ui-icons_222222_256x240.png +0 -0
  66. data/lib/pulse_meter/visualize/public/css/images/ui-icons_2e83ff_256x240.png +0 -0
  67. data/lib/pulse_meter/visualize/public/css/images/ui-icons_454545_256x240.png +0 -0
  68. data/lib/pulse_meter/visualize/public/css/images/ui-icons_888888_256x240.png +0 -0
  69. data/lib/pulse_meter/visualize/public/css/images/ui-icons_cd0a0a_256x240.png +0 -0
  70. data/lib/pulse_meter/visualize/public/css/images/ui-icons_f6cf3b_256x240.png +0 -0
  71. data/lib/pulse_meter/visualize/public/css/jquery-ui-1.8.16.bootstrap.css +1320 -0
  72. data/lib/pulse_meter/visualize/public/favicon.ico +208 -0
  73. data/lib/pulse_meter/visualize/public/img/glyphicons-halflings-white.png +0 -0
  74. data/lib/pulse_meter/visualize/public/img/glyphicons-halflings.png +0 -0
  75. data/lib/pulse_meter/visualize/public/js/application.js +973 -0
  76. data/lib/pulse_meter/visualize/public/js/backbone-min.js +38 -0
  77. data/lib/pulse_meter/visualize/public/js/bootstrap.js +1835 -0
  78. data/lib/pulse_meter/visualize/public/js/jquery-1.7.2.min.js +4 -0
  79. data/lib/pulse_meter/visualize/public/js/jquery-ui-1.8.16.bootstrap.min.js +791 -0
  80. data/lib/pulse_meter/visualize/public/js/jquery-ui-1.8.23.custom.min.js +21 -0
  81. data/lib/pulse_meter/visualize/public/js/jquery-ui-timepicker-addon.js +1687 -0
  82. data/lib/pulse_meter/visualize/public/js/json2.js +487 -0
  83. data/lib/pulse_meter/visualize/public/js/underscore-min.js +32 -0
  84. data/lib/pulse_meter/visualize/sensor.rb +63 -0
  85. data/lib/pulse_meter/visualize/series_extractor.rb +107 -0
  86. data/lib/pulse_meter/visualize/views/main.haml +30 -0
  87. data/lib/pulse_meter/visualize/views/sensors.haml +76 -0
  88. data/lib/pulse_meter/visualize/views/widgets/area.haml +53 -0
  89. data/lib/pulse_meter/visualize/views/widgets/extend_options.haml +11 -0
  90. data/lib/pulse_meter/visualize/views/widgets/gauge.haml +13 -0
  91. data/lib/pulse_meter/visualize/views/widgets/line.haml +54 -0
  92. data/lib/pulse_meter/visualize/views/widgets/pie.haml +13 -0
  93. data/lib/pulse_meter/visualize/views/widgets/table.haml +45 -0
  94. data/lib/pulse_meter/visualize/widget.rb +38 -0
  95. data/lib/pulse_meter/visualize/widgets/gauge.rb +47 -0
  96. data/lib/pulse_meter/visualize/widgets/pie.rb +36 -0
  97. data/lib/pulse_meter/visualize/widgets/timeline.rb +114 -0
  98. data/lib/pulse_meter/visualizer.rb +38 -0
  99. data/lib/pulse_meter_visualizer.rb +2 -0
  100. data/pulse_meter_visualizer.gemspec +41 -0
  101. data/spec/pulse_meter/visualize/app_spec.rb +27 -0
  102. data/spec/pulse_meter/visualize/dsl/layout_spec.rb +64 -0
  103. data/spec/pulse_meter/visualize/dsl/page_spec.rb +62 -0
  104. data/spec/pulse_meter/visualize/dsl/sensor_spec.rb +30 -0
  105. data/spec/pulse_meter/visualize/dsl/widget_spec.rb +6 -0
  106. data/spec/pulse_meter/visualize/dsl/widgets/area_spec.rb +44 -0
  107. data/spec/pulse_meter/visualize/dsl/widgets/gauge_spec.rb +22 -0
  108. data/spec/pulse_meter/visualize/dsl/widgets/line_spec.rb +44 -0
  109. data/spec/pulse_meter/visualize/dsl/widgets/pie_spec.rb +35 -0
  110. data/spec/pulse_meter/visualize/dsl/widgets/table_spec.rb +36 -0
  111. data/spec/pulse_meter/visualize/layout_spec.rb +54 -0
  112. data/spec/pulse_meter/visualize/page_spec.rb +153 -0
  113. data/spec/pulse_meter/visualize/sensor_spec.rb +120 -0
  114. data/spec/pulse_meter/visualize/series_extractor_spec.rb +80 -0
  115. data/spec/pulse_meter/visualize/widgets/area_spec.rb +6 -0
  116. data/spec/pulse_meter/visualize/widgets/gauge_spec.rb +63 -0
  117. data/spec/pulse_meter/visualize/widgets/line_spec.rb +6 -0
  118. data/spec/pulse_meter/visualize/widgets/pie_spec.rb +73 -0
  119. data/spec/pulse_meter/visualize/widgets/table_spec.rb +6 -0
  120. data/spec/pulse_meter/visualizer_spec.rb +42 -0
  121. data/spec/shared_examples/dsl_widget.rb +106 -0
  122. data/spec/shared_examples/widget.rb +97 -0
  123. data/spec/spec_helper.rb +36 -0
  124. metadata +518 -0
@@ -0,0 +1,58 @@
1
+ DynamicWidgetView = Backbone.View.extend {
2
+ tagName: 'div'
3
+
4
+ initialize: (options) ->
5
+ @pageInfos = options['pageInfos']
6
+ @sensorInfo = new SensorInfoList
7
+
8
+ @sensorListView = new SensorInfoListView(@sensorInfo)
9
+ @chartView = new DynamicChartView {pageInfos: @pageInfos}
10
+
11
+ @$el.html(@template()())
12
+
13
+ @$el.find('#sensor-list-area').append(@sensorListView.el)
14
+
15
+ @chartView.render()
16
+ @$el.find('#dynamic-plotarea').append(@chartView.el)
17
+
18
+ events: {
19
+ "click #sensor-controls #refresh": 'refresh'
20
+ "click #sensor-controls #draw": 'drawChart'
21
+ }
22
+
23
+ template: ->
24
+ _.template($("#dynamic-widget").html())
25
+
26
+ errorTemplate: -> _.template($("#dynamic-widget-error").html())
27
+
28
+ error: (error)->
29
+ @$el.find('#errors').append(@errorTemplate()(error: error))
30
+
31
+ refresh: ->
32
+ @sensorInfo.fetch()
33
+
34
+ intervalsEqual: (sensors) ->
35
+ interval = sensors[0].get('interval')
36
+ badIntervals = _.filter(sensors, (s) ->
37
+ s.get('interval') != interval
38
+ )
39
+ badIntervals.length == 0
40
+
41
+ drawChart: ->
42
+ selectedSensors = @sensorListView.selectedSensors()
43
+ return unless selectedSensors.length > 0
44
+
45
+ unless @intervalsEqual(selectedSensors)
46
+ @error('Selected sensors have different intervals')
47
+ return
48
+
49
+ type = @$el.find('#chart-type').val()
50
+ @chartView.draw(selectedSensors, type)
51
+
52
+ render: (container) ->
53
+ container.empty()
54
+ container.append(@$el)
55
+ @sensorInfo.fetch()
56
+ @chartView.update()
57
+
58
+ }
@@ -0,0 +1,17 @@
1
+ PageTitleView = Backbone.View.extend {
2
+ tagName: 'li'
3
+
4
+ template: _.template('<a href="#/pages/<%= id %>"><%= title %></a>')
5
+
6
+ initialize: ->
7
+ @model.bind 'change', @render, this
8
+ @model.bind 'destroy', @remove, this
9
+
10
+ render: ->
11
+ @$el.html @template(@model.toJSON())
12
+ if @model.get('selected')
13
+ @$el.addClass('active')
14
+ else
15
+ @$el.removeClass('active')
16
+ }
17
+
@@ -0,0 +1,15 @@
1
+ PageTitlesView = Backbone.View.extend {
2
+ initialize: (@pageInfos) ->
3
+ @pageInfos.bind 'reset', @render, this
4
+
5
+ addOne: (pageInfo) ->
6
+ view = new PageTitleView {
7
+ model: pageInfo
8
+ }
9
+ view.render()
10
+ $('#page-titles').append(view.el)
11
+
12
+ render: ->
13
+ $('#page-titles').empty()
14
+ @pageInfos.each(@addOne)
15
+ }
@@ -0,0 +1,19 @@
1
+ SensorInfoListView = Backbone.View.extend {
2
+ tagName: 'div'
3
+
4
+ template: ->
5
+ _.template($("#sensor-list").html())
6
+
7
+ initialize: (sensorInfo) ->
8
+ @sensorInfo = sensorInfo
9
+ @sensorInfo.bind 'reset', @render, this
10
+
11
+ render: ->
12
+ @$el.html @template()({sensors: @sensorInfo.toJSON()})
13
+
14
+ selectedSensors: ->
15
+ checked = _.filter @$el.find('.sensor-box'), (el) -> $(el).is(':checked')
16
+ ids = {}
17
+ _.each checked, (box) -> ids[box.id] = true
18
+ selected = @sensorInfo.filter (sensor) -> ids[sensor.id]
19
+ }
@@ -0,0 +1,99 @@
1
+ WidgetView = Backbone.View.extend {
2
+ tagName: 'div'
3
+
4
+ template: (args) ->
5
+ _.template($(".widget-template[data-widget-type=\"#{@model.get('type')}\"]").html())(args)
6
+
7
+ initialize: (options) ->
8
+ @pageInfos = options['pageInfos']
9
+ @model.bind('destroy', @remove, this)
10
+ @model.bind('redraw', @updateChart, this)
11
+
12
+ events: {
13
+ "click #refresh": 'refresh'
14
+ "click #need-refresh": 'setRefresh'
15
+ "click #extend-timespan": 'extendTimespan'
16
+ "click #reset-timespan": 'resetTimespan'
17
+ "change #start-time input": 'maybeEnableStopTime'
18
+ "click #set-interval": 'setTimelineInterval'
19
+ }
20
+
21
+ refresh: ->
22
+ @model.forceUpdate()
23
+
24
+ setRefresh: ->
25
+ needRefresh = @$el.find('#need-refresh').is(":checked")
26
+ @model.setRefresh(needRefresh)
27
+ true
28
+
29
+ extendTimespan: ->
30
+ select = @$el.find("#extend-timespan-val")
31
+ val = select.first().val()
32
+ @model.increaseTimespan(parseInt(val))
33
+
34
+ setTimelineInterval: ->
35
+ start = @unixtimeFromDatepicker("#start-time input")
36
+ end = @unixtimeFromDatepicker("#end-time input")
37
+ @model.setStartTime(start)
38
+ @model.setEndTime(end)
39
+
40
+ maybeEnableStopTime: ->
41
+ date = @dateFromDatepicker("#start-time input")
42
+ disabled = if date then false else true
43
+ @$el.find("#end-time input").prop("disabled", disabled)
44
+
45
+ resetTimespan: ->
46
+ @model.resetTimespan()
47
+
48
+ renderChart: ->
49
+ @chartView.render()
50
+
51
+ updateChart: ->
52
+ @chartView.updateData(@cutoffMin(), @cutoffMax())
53
+
54
+ setIds: ->
55
+ @$el.find('#configure-button').prop('href', "#configure-#{@model.id}")
56
+ @$el.find('#configure').attr('id', "configure-#{@model.id}")
57
+ @$el.find('#start-time input').attr('id', "start-time-#{@model.id}")
58
+ @$el.find('#end-time input').attr('id', "end-time-#{@model.id}")
59
+
60
+ render: ->
61
+ @$el.html @template(@model.toJSON())
62
+ @setIds()
63
+ @chartView = new WidgetChartView {
64
+ pageInfos: @pageInfos
65
+ model: @model
66
+ }
67
+ @$el.find("#plotarea").append(@chartView.el)
68
+ @$el.addClass("span#{@model.get('width')}")
69
+ @initDatePickers()
70
+
71
+ initDatePickers: ->
72
+ @$el.find(".datepicker").each (i) ->
73
+ $(this).datetimepicker
74
+ showOtherMonths: true
75
+ selectOtherMonths: true
76
+ @$el.find("#end-time input").prop("disabled", true)
77
+
78
+ cutoffMin: ->
79
+ val = parseFloat(@controlValue('#cutoff-min'))
80
+ if _.isNaN(val) then null else val
81
+
82
+ cutoffMax: ->
83
+ val = parseFloat(@controlValue('#cutoff-max'))
84
+ if _.isNaN(val) then null else val
85
+
86
+ controlValue: (id) ->
87
+ val = @$el.find(id).first().val()
88
+
89
+ dateFromDatepicker: (id) ->
90
+ @$el.find(id).datetimepicker("getDate")
91
+
92
+ unixtimeFromDatepicker: (id) ->
93
+ date = @dateFromDatepicker(id)
94
+ if date
95
+ date.getTime() / 1000
96
+ else
97
+ null
98
+
99
+ }
@@ -0,0 +1,13 @@
1
+ WidgetChartView = Backbone.View.extend {
2
+ tagName: 'div'
3
+
4
+ initialize: (options) ->
5
+ @pageInfos = options['pageInfos']
6
+ @model.bind 'destroy', @remove, this
7
+
8
+ updateData: (min, max) ->
9
+ @presenter.draw(min, max)
10
+
11
+ render: ->
12
+ @presenter = WidgetPresenter.create(@pageInfos, @model, @el)
13
+ }
@@ -0,0 +1,15 @@
1
+ WidgetListView = Backbone.View.extend {
2
+ initialize: (options) ->
3
+ @widgetList = options['widgetList']
4
+ @pageInfos = options['pageInfos']
5
+ @widgetList.bind 'reset', @render, this
6
+
7
+ render: ->
8
+ container = $('#widgets')
9
+ container.empty()
10
+ @widgetList.each (w) =>
11
+ view = new WidgetView { pageInfos: @pageInfos, model: w }
12
+ view.render()
13
+ container.append(view.el)
14
+ view.renderChart()
15
+ }
@@ -0,0 +1,131 @@
1
+ module PulseMeter
2
+ module Visualize
3
+ module DSL
4
+ class DArray < Array; end
5
+ class BadDataClass < PulseMeter::Visualize::DSL::Error; end
6
+ class Base
7
+ include PulseMeter::Mixins::Utils
8
+
9
+ def initialize
10
+ @opts = {}
11
+ end
12
+
13
+ def process_args(args)
14
+ args.each_pair do |k, v|
15
+ send(k, v)
16
+ end
17
+ end
18
+
19
+ class << self
20
+
21
+ def deprecated_setter(name)
22
+ define_method(name) do |*args|
23
+ STDERR.puts "DEPRECATION: #{name} DSL helper does not take any effect anymore."
24
+ end
25
+ end
26
+
27
+ def setter(name, &block)
28
+ define_method(name) do |val|
29
+ block.call(val) if block
30
+ @opts[name] = val
31
+ end
32
+ end
33
+
34
+ def bool_setter(name)
35
+ define_method(name) do |val|
36
+ @opts[name] = !!val
37
+ end
38
+ end
39
+
40
+ def string_setter(name, &block)
41
+ define_method(name) do |val|
42
+ val = val.to_s
43
+ block.call(val) if block
44
+ @opts[name] = val
45
+ end
46
+ end
47
+
48
+ def int_setter(name, &block)
49
+ define_method(name) do |val|
50
+ val = val.to_i
51
+ block.call(val) if block
52
+ @opts[name] = val
53
+ end
54
+ end
55
+
56
+ def hash_extender(name, &block)
57
+ define_method(name) do |val|
58
+ @opts[name] ||= {}
59
+ @opts[name].merge!(val)
60
+ end
61
+ end
62
+
63
+ def array_extender(name, &block)
64
+ define_method(name) do |val|
65
+ @opts[name] ||=[]
66
+ block.call(val) if block
67
+ @opts[name] << val
68
+ end
69
+ end
70
+
71
+ def dsl_setter(name, klass)
72
+ define_method(name) do |*args, &block|
73
+ @opts[name] = create_dsl_obj(args, klass, block)
74
+ end
75
+ end
76
+
77
+ def dsl_array_extender(collection_name, name, klass)
78
+ define_method(name) do |*args, &block|
79
+ @opts[collection_name] ||= DArray.new
80
+ @opts[collection_name] << create_dsl_obj(args, klass, block)
81
+ end
82
+ end
83
+
84
+ def data_class
85
+ @data_class || PulseMeter::Visualize::Base
86
+ end
87
+
88
+ def data_class=(klass)
89
+ raise BadDataClass unless klass.is_a?(Class) && klass <= PulseMeter::Visualize::Base
90
+ @data_class = klass
91
+ end
92
+
93
+ end
94
+
95
+ def create_dsl_obj(args, klass, block)
96
+ params, options = extract_params(args)
97
+ dsl_obj = klass.new(*params)
98
+ dsl_obj.process_args(options)
99
+ block.call(dsl_obj) if block
100
+ dsl_obj
101
+ end
102
+
103
+ def extract_params(args)
104
+ opts = if args.last.is_a?(Hash)
105
+ args.pop
106
+ else
107
+ {}
108
+ end
109
+ [args, opts]
110
+ end
111
+
112
+ def to_data
113
+ klass = self.class.data_class
114
+ args = @opts.each_with_object({}) do |(k, v), acc|
115
+ acc[k] = case v
116
+ when PulseMeter::Visualize::DSL::Base
117
+ v.to_data
118
+ when DArray
119
+ v.map(&:to_data)
120
+ else
121
+ v
122
+ end
123
+ end
124
+ klass.new(args)
125
+ end
126
+
127
+ end
128
+ end
129
+ end
130
+ end
131
+
@@ -0,0 +1,40 @@
1
+ module PulseMeter
2
+ module Visualize
3
+ module DSL
4
+ class Error < StandardError; end
5
+
6
+ class NoWidgets < Error; end
7
+
8
+ class BadSensorName < Error
9
+ def initialize(name)
10
+ super("Bad sensor name: `#{name}'")
11
+ end
12
+ end
13
+
14
+ class BadWidgetName < Error
15
+ def initialize(name)
16
+ super("Bad widget name: `#{name}'")
17
+ end
18
+ end
19
+
20
+ class BadWidgetWidth < Error
21
+ def initialize(width)
22
+ super("Bad widget width: `#{width}'")
23
+ end
24
+ end
25
+
26
+ class BadWidgetRedrawInterval < Error
27
+ def initialize(redraw_interval)
28
+ super("Bad widget redraw_interval: `#{redraw_interval}'")
29
+ end
30
+ end
31
+
32
+ class BadWidgetTimeSpan < Error
33
+ def initialize(timespan)
34
+ super("Bad widget timespan: `#{timespan}'")
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
@@ -0,0 +1,27 @@
1
+ module PulseMeter
2
+ module Visualize
3
+ module DSL
4
+ class Layout < Base
5
+ DEFAULT_TITLE = "Pulse Meter"
6
+
7
+ self.data_class = PulseMeter::Visualize::Layout
8
+
9
+ def initialize
10
+ super()
11
+ self.title(DEFAULT_TITLE)
12
+ self.use_utc(false)
13
+ end
14
+
15
+ string_setter :title
16
+ bool_setter :use_utc
17
+ hash_extender :gchart_options
18
+
19
+ deprecated_setter :outlier_color
20
+ deprecated_setter :highchart_options
21
+
22
+ dsl_array_extender :pages, :page, PulseMeter::Visualize::DSL::Page
23
+ end
24
+ end
25
+ end
26
+ end
27
+