focuslight 0.1.1
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/.env +13 -0
- data/.gitignore +21 -0
- data/.travis.yml +9 -0
- data/CHANGELOG.md +21 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/Procfile +3 -0
- data/Procfile-gem +3 -0
- data/README.md +162 -0
- data/Rakefile +37 -0
- data/bin/focuslight +7 -0
- data/config.ru +6 -0
- data/focuslight.gemspec +41 -0
- data/lib/focuslight.rb +6 -0
- data/lib/focuslight/cli.rb +56 -0
- data/lib/focuslight/config.rb +27 -0
- data/lib/focuslight/data.rb +258 -0
- data/lib/focuslight/graph.rb +240 -0
- data/lib/focuslight/init.rb +13 -0
- data/lib/focuslight/logger.rb +89 -0
- data/lib/focuslight/rrd.rb +393 -0
- data/lib/focuslight/validator.rb +220 -0
- data/lib/focuslight/version.rb +3 -0
- data/lib/focuslight/web.rb +614 -0
- data/lib/focuslight/worker.rb +97 -0
- data/public/css/bootstrap.min.css +7 -0
- data/public/favicon.ico +0 -0
- data/public/fonts/glyphicons-halflings-regular.eot +0 -0
- data/public/fonts/glyphicons-halflings-regular.svg +229 -0
- data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/public/fonts/glyphicons-halflings-regular.woff +0 -0
- data/public/js/bootstrap.min.js +7 -0
- data/public/js/jquery-1.10.2.min.js +6 -0
- data/public/js/jquery-1.10.2.min.map +0 -0
- data/public/js/jquery.storageapi.min.js +2 -0
- data/public/js/site.js +214 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/syntax_spec.rb +9 -0
- data/spec/validator_predefined_rules_spec.rb +177 -0
- data/spec/validator_result_spec.rb +27 -0
- data/spec/validator_rule_spec.rb +68 -0
- data/spec/validator_spec.rb +121 -0
- data/view/add_complex.erb +143 -0
- data/view/base.erb +200 -0
- data/view/docs.erb +125 -0
- data/view/edit.erb +102 -0
- data/view/edit_complex.erb +158 -0
- data/view/index.erb +19 -0
- data/view/list.erb +22 -0
- data/view/view.erb +42 -0
- data/view/view_graph.erb +16 -0
- metadata +345 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
require "focuslight"
|
2
|
+
|
3
|
+
module Focuslight::Config
|
4
|
+
DEFAULT_DATADIR = File.expand_path('data', "#{__dir__}/../..")
|
5
|
+
DEFAULT_LOG_PATH = File.expand_path('log/application.log', "#{__dir__}/../..")
|
6
|
+
|
7
|
+
def self.get(name)
|
8
|
+
case name
|
9
|
+
when :datadir
|
10
|
+
ENV.fetch('DATADIR', DEFAULT_DATADIR)
|
11
|
+
when :float_support
|
12
|
+
ENV.fetch('FLOAT_SUPPORT', false)
|
13
|
+
when :dburl
|
14
|
+
ENV.fetch('DBURL', 'sqlite://data/gforecast.db')
|
15
|
+
when :log_path
|
16
|
+
ENV.fetch('LOG_PATH', DEFAULT_LOG_PATH)
|
17
|
+
when :log_level
|
18
|
+
ENV.fetch('LOG_LEVEL', 'info')
|
19
|
+
when :log_shift_age
|
20
|
+
ENV.fetch('LOG_SHIFT_AGE', '0')
|
21
|
+
when :log_shift_size
|
22
|
+
ENV.fetch('LOG_SHIFT_SIZE', '1048576')
|
23
|
+
else
|
24
|
+
raise ArgumentError, 'unknown configuration keyword'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,258 @@
|
|
1
|
+
require "focuslight"
|
2
|
+
require "focuslight/config"
|
3
|
+
require "focuslight/logger"
|
4
|
+
require "focuslight/graph"
|
5
|
+
require "sequel"
|
6
|
+
|
7
|
+
DB = Sequel.connect(Focuslight::Config.get(:dburl), logger: Focuslight.logger)
|
8
|
+
|
9
|
+
class Focuslight::Data
|
10
|
+
include Focuslight::Logger
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@datadir = Focuslight::Config.get(:datadir)
|
14
|
+
@floatings = Focuslight::Config.get(:float_support) == "y"
|
15
|
+
|
16
|
+
if DB.database_type == :sqlite
|
17
|
+
DB.run 'PRAGMA journal_mode = WAL'
|
18
|
+
DB.run 'PRAGMA synchronous = NORMAL'
|
19
|
+
end
|
20
|
+
@graphs = DB.from(:graphs)
|
21
|
+
@complex_graphs = DB.from(:complex_graphs)
|
22
|
+
end
|
23
|
+
|
24
|
+
def number_type
|
25
|
+
@floatings ? Float : Bignum
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_tables
|
29
|
+
ntype = number_type
|
30
|
+
DB.transaction do
|
31
|
+
|
32
|
+
DB.create_table :graphs do
|
33
|
+
primary_key :id, Bignum # Notice that SQLite actually creates integer primary key
|
34
|
+
column :service_name, String, null: false
|
35
|
+
column :section_name, String, null: false
|
36
|
+
column :graph_name, String, null: false
|
37
|
+
column :number, ntype, default: 0
|
38
|
+
column :mode, String, default: "gauge", null: false
|
39
|
+
column :description, String, default: "", null: false
|
40
|
+
column :sort, Bignum, default: 0, null: false
|
41
|
+
column :color, String, default: "#00CC00", null: false
|
42
|
+
column :ulimit, ntype, default: 1000000000000000, null: false
|
43
|
+
column :llimit, ntype, default: 0, null: false
|
44
|
+
column :type, String, default: "AREA", null: false
|
45
|
+
String :meta, text: true
|
46
|
+
column :created_at, Bignum, null: false
|
47
|
+
column :updated_at, Bignum, null: false
|
48
|
+
unique [:service_name, :section_name, :graph_name]
|
49
|
+
end
|
50
|
+
|
51
|
+
DB.create_table :complex_graphs do
|
52
|
+
primary_key :id, Bignum # Notice that SQLite actually creates integer primary key
|
53
|
+
column :service_name, String, null: false
|
54
|
+
column :section_name, String, null: false
|
55
|
+
column :graph_name, String, null: false
|
56
|
+
column :number, ntype, default: 0
|
57
|
+
column :description, String, default: "", null: false
|
58
|
+
column :sort, Bignum, default: 0, null: false
|
59
|
+
String :meta, text: true
|
60
|
+
column :created_at, Bignum, null: false
|
61
|
+
column :updated_at, Bignum, null: false
|
62
|
+
unique [:service_name, :section_name, :graph_name]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def execute(*args)
|
68
|
+
DB.run(*args)
|
69
|
+
end
|
70
|
+
|
71
|
+
def transaction
|
72
|
+
DB.transaction do
|
73
|
+
yield DB
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def get(service, section, graph)
|
78
|
+
data = @graphs.where(service_name: service, section_name: section, graph_name: graph).first
|
79
|
+
data && Focuslight::Graph.concrete(data)
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_by_id(id)
|
83
|
+
data = @graphs[id: id]
|
84
|
+
data && Focuslight::Graph.concrete(data)
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_by_id_for_rrdupdate(id, target=:normal) # get_by_id_for_rrdupdate_short == get_by_id_for_rrdupdate(id, :short)
|
88
|
+
data = @graphs[id: id]
|
89
|
+
return nil unless data
|
90
|
+
graph = Focuslight::Graph.concrete(data)
|
91
|
+
end
|
92
|
+
|
93
|
+
def update(service_name, section_name, graph_name, number, mode, color)
|
94
|
+
data = nil
|
95
|
+
DB.transaction do
|
96
|
+
data = @graphs.where(service_name: service_name, section_name: section_name, graph_name: graph_name).first
|
97
|
+
if data
|
98
|
+
graph = Focuslight::Graph.concrete(data)
|
99
|
+
if mode == 'count'
|
100
|
+
number += graph.number
|
101
|
+
end
|
102
|
+
if mode != 'modified' || (mode == 'modified' && graph.number != number)
|
103
|
+
color = graph.color if color.empty?
|
104
|
+
@graphs.where(id: graph.id).update(number: number, mode: mode, color: color, updated_at: Time.now.to_i)
|
105
|
+
end
|
106
|
+
else
|
107
|
+
color = '#' + ['33', '66', '99', 'cc'].shuffle.slice(0,3).join if color.empty?
|
108
|
+
# COLUMNS = %w(service_name section_name graph_name number mode color llimit created_at updated_at)
|
109
|
+
columns = Focuslight::SimpleGraph::COLUMNS.join(',')
|
110
|
+
# PLACEHOLDERS = COLUMNS.map{|c| '?'}
|
111
|
+
placeholders = Focuslight::SimpleGraph::PLACEHOLDERS.join(',')
|
112
|
+
current_time = Time.now.to_i
|
113
|
+
@graphs.insert(
|
114
|
+
service_name: service_name,
|
115
|
+
section_name: section_name,
|
116
|
+
graph_name: graph_name,
|
117
|
+
number: number,
|
118
|
+
mode: mode,
|
119
|
+
color: color,
|
120
|
+
llimit: -1000000000,
|
121
|
+
created_at: current_time,
|
122
|
+
updated_at: current_time)
|
123
|
+
end
|
124
|
+
|
125
|
+
data = @graphs.where(service_name: service_name, section_name: section_name, graph_name: graph_name).first
|
126
|
+
end # transaction
|
127
|
+
|
128
|
+
Focuslight::Graph.concrete(data)
|
129
|
+
end
|
130
|
+
|
131
|
+
def update_graph(id, args)
|
132
|
+
graph = get_by_id(id)
|
133
|
+
return nil unless graph
|
134
|
+
|
135
|
+
graph.update(args)
|
136
|
+
@graphs.where(id: graph.id)
|
137
|
+
.update(
|
138
|
+
service_name: graph.service,
|
139
|
+
section_name: graph.section,
|
140
|
+
graph_name: graph.graph,
|
141
|
+
description: graph.description,
|
142
|
+
sort: graph.sort,
|
143
|
+
color: graph.color,
|
144
|
+
type: graph.type,
|
145
|
+
llimit: graph.llimit,
|
146
|
+
ulimit: graph.ulimit,
|
147
|
+
meta: graph.meta
|
148
|
+
)
|
149
|
+
true
|
150
|
+
end
|
151
|
+
|
152
|
+
def update_graph_description(id, description)
|
153
|
+
@graphs.where(id: id).update(description: description)
|
154
|
+
true
|
155
|
+
end
|
156
|
+
|
157
|
+
def get_services
|
158
|
+
rows1 = @graphs.order(:service_name).all
|
159
|
+
rows2 = @complex_graphs.order(:service_name).all
|
160
|
+
(rows1 + rows2).map{|row| row[:service_name]}.uniq.sort
|
161
|
+
end
|
162
|
+
|
163
|
+
def get_sections(service)
|
164
|
+
rows1 = @graphs.select(:section_name).order(:section_name).where(service_name: service).all
|
165
|
+
rows2 = @complex_graphs.select(:section_name).order(:section_name).where(service_name: service).all
|
166
|
+
(rows1 + rows2).map{|row| row[:section_name]}.uniq.sort
|
167
|
+
end
|
168
|
+
|
169
|
+
def get_graphs(service, section)
|
170
|
+
rows1 = @graphs.order(Sequel.desc(:sort)).where(service_name: service, section_name: section).all
|
171
|
+
rows2 = @complex_graphs.order(Sequel.desc(:sort)).where(service_name: service, section_name: section).all
|
172
|
+
(rows1 + rows2).map{|row| Focuslight::Graph.concrete(row)}.sort{|a,b| b.sort <=> a.sort}
|
173
|
+
end
|
174
|
+
|
175
|
+
def get_all_graph_id
|
176
|
+
@graphs.select(:id).all
|
177
|
+
end
|
178
|
+
|
179
|
+
def get_all_graph_name
|
180
|
+
@graphs.select(:id, :service_name, :section_name, :graph_name).reverse_order(:service_name, :section_name, :graph_name).all
|
181
|
+
end
|
182
|
+
|
183
|
+
def get_all_graph_all
|
184
|
+
rows = @graphs.reverse_order(:service_name, :section_name, :graph_name).all
|
185
|
+
rows.map{|row| Focuslight::Graph.concrete(row)}
|
186
|
+
end
|
187
|
+
|
188
|
+
def remove(id)
|
189
|
+
DB.transaction do
|
190
|
+
@graphs.where(id: id).delete
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def get_complex(service, section, graph)
|
195
|
+
data = @complex_graphs.where(service_name: service, section_name: section, graph_name: graph).first
|
196
|
+
data && Focuslight::Graph.concrete(data)
|
197
|
+
end
|
198
|
+
|
199
|
+
def get_complex_by_id(id)
|
200
|
+
data = @complex_graphs.where(id: id).first
|
201
|
+
data && Focuslight::Graph.concrete(data)
|
202
|
+
end
|
203
|
+
|
204
|
+
def create_complex(service, section, graph, args)
|
205
|
+
description = args[:description]
|
206
|
+
sort = args[:sort]
|
207
|
+
meta = Focuslight::ComplexGraph.meta_clean(args).to_json
|
208
|
+
now = Time.now.to_i
|
209
|
+
@complex_graphs.insert(
|
210
|
+
service_name: service,
|
211
|
+
section_name: section,
|
212
|
+
graph_name: graph,
|
213
|
+
description: description,
|
214
|
+
sort: sort.to_i,
|
215
|
+
meta: meta,
|
216
|
+
created_at: now,
|
217
|
+
updated_at: now,
|
218
|
+
)
|
219
|
+
get_complex(service, section, graph)
|
220
|
+
end
|
221
|
+
|
222
|
+
def update_complex(id, args)
|
223
|
+
graph = get_complex_by_id(id)
|
224
|
+
return nil unless graph
|
225
|
+
|
226
|
+
graph.update(args)
|
227
|
+
@complex_graphs.where(id: graph.id)
|
228
|
+
.update(
|
229
|
+
service_name: graph.service,
|
230
|
+
section_name: graph.section,
|
231
|
+
graph_name: graph.graph,
|
232
|
+
description: graph.description,
|
233
|
+
sort: graph.sort,
|
234
|
+
meta: graph.meta,
|
235
|
+
updated_at: Time.now.to_i,
|
236
|
+
)
|
237
|
+
|
238
|
+
get_complex_by_id(id)
|
239
|
+
end
|
240
|
+
|
241
|
+
def remove_complex(id)
|
242
|
+
@complex_graphs.where(id: id).delete
|
243
|
+
end
|
244
|
+
|
245
|
+
def get_all_complex_graph_id
|
246
|
+
@complex_graphs.select(:id).all
|
247
|
+
end
|
248
|
+
|
249
|
+
def get_all_complex_graph_name
|
250
|
+
@complex_graphs.select(:id, :service_name, :section_name, :graph_name).reverse_order(:service_name, :section_name, :graph_name).all
|
251
|
+
end
|
252
|
+
|
253
|
+
def get_all_complex_graph_all
|
254
|
+
rows = @complex_graphs.reverse_order(:service_name, :section_name, :graph_name)
|
255
|
+
return [] unless rows
|
256
|
+
rows.map{|row| Focuslight::Graph.concrete(row)}
|
257
|
+
end
|
258
|
+
end
|
@@ -0,0 +1,240 @@
|
|
1
|
+
require "focuslight"
|
2
|
+
require "focuslight/logger"
|
3
|
+
|
4
|
+
require "digest"
|
5
|
+
require "json"
|
6
|
+
|
7
|
+
module Focuslight
|
8
|
+
class Graph
|
9
|
+
include Focuslight::Logger
|
10
|
+
|
11
|
+
def self.concrete(row)
|
12
|
+
if row.has_key?(:mode) && row.has_key?(:type)
|
13
|
+
Focuslight::SimpleGraph.new(row)
|
14
|
+
else
|
15
|
+
Focuslight::ComplexGraph.new(row)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_accessor :id, :service, :section, :graph, :number, :description, :sort
|
20
|
+
attr_accessor :meta
|
21
|
+
attr_accessor :created_at_time, :updated_at_time
|
22
|
+
|
23
|
+
attr_accessor :c_type, :stack # for complex graph construction
|
24
|
+
|
25
|
+
def initialize(row)
|
26
|
+
@row_hash = row
|
27
|
+
|
28
|
+
@id = row[:id]
|
29
|
+
@service = row[:service_name]
|
30
|
+
@section = row[:section_name]
|
31
|
+
@graph = row[:graph_name]
|
32
|
+
@number = row[:number].to_i # NOT NULL DEFAULT 0
|
33
|
+
@description = row[:description] || ''
|
34
|
+
@sort = row[:sort].to_i # NOT NULL DEFAULT 0
|
35
|
+
|
36
|
+
@meta = row[:meta]
|
37
|
+
@parsed_meta = JSON.parse(@meta || '{}', :symbolize_names => true)
|
38
|
+
|
39
|
+
@created_at_time = Time.at(row[:created_at].to_i)
|
40
|
+
@updated_at_time = Time.at(row[:updated_at].to_i)
|
41
|
+
end
|
42
|
+
|
43
|
+
def path
|
44
|
+
[@service, @section, @graph]
|
45
|
+
end
|
46
|
+
|
47
|
+
def created_at
|
48
|
+
@created_at_time.strftime('%Y/%m/%d %T')
|
49
|
+
end
|
50
|
+
|
51
|
+
def updated_at
|
52
|
+
@updated_at_time.strftime('%Y/%m/%d %T')
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_hash
|
56
|
+
{
|
57
|
+
id: @id, service_name: @service, section_name: @section, graph_name: @graph,
|
58
|
+
number: @number, description: @description, sort: @sort,
|
59
|
+
created_at: self.created_at(), updated_at: self.updated_at(),
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.hash2request(hash)
|
64
|
+
hash = hash.dup
|
65
|
+
is_complex = hash.delete(:complex)
|
66
|
+
|
67
|
+
hash.delete(:id)
|
68
|
+
hash.delete(:created_at)
|
69
|
+
hash.delete(:updated_at)
|
70
|
+
|
71
|
+
return hash unless is_complex ##TODO concrete?
|
72
|
+
|
73
|
+
hash.delete(:number)
|
74
|
+
hash[:sumup] = (hash[:sumup] ? '1' : '0')
|
75
|
+
|
76
|
+
data_rows = hash.delete(:data)
|
77
|
+
|
78
|
+
first = data_rows.shift
|
79
|
+
hash['path-1'.to_sym] = first[:graph_id]
|
80
|
+
hash['type-1'.to_sym] = first[:type]
|
81
|
+
|
82
|
+
p2 = 'path-2'.to_sym
|
83
|
+
t2 = 'type-2'.to_sym
|
84
|
+
s2 = 'stack-2'.to_sym
|
85
|
+
hash[p2] = []
|
86
|
+
hash[t2] = []
|
87
|
+
hash[s2] = []
|
88
|
+
data_rows.each do |row|
|
89
|
+
hash[p2] << row[:graph_id]
|
90
|
+
hash[t2] << row[:type]
|
91
|
+
hash[s2] << (row[:stack] ? '1' : '0')
|
92
|
+
end
|
93
|
+
|
94
|
+
hash ##TODO concrete?
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class SimpleGraph < Graph
|
99
|
+
COLUMNS = %w(service_name section_name graph_name number mode color llimit created_at updated_at)
|
100
|
+
PLACEHOLDERS = COLUMNS.map{|c| '?'}
|
101
|
+
|
102
|
+
attr_accessor :mode, :color, :ulimit, :llimit, :type
|
103
|
+
|
104
|
+
attr_reader :md5
|
105
|
+
attr_accessor :adjust, :adjustval, :unit
|
106
|
+
|
107
|
+
def initialize(row)
|
108
|
+
super
|
109
|
+
|
110
|
+
@mode = row[:mode] || 'gauge' # NOT NULL DEFAULT 'gauge'
|
111
|
+
@color = row[:color] || '#00CC00' # NOT NULL DEFAULT '#00CC00'
|
112
|
+
@ulimit = row[:ulimit] || 1000000000000000 # NOT NULL DEFAULT 1000000000000000
|
113
|
+
@llimit = row[:llimit] || 0
|
114
|
+
@type = row[:type] || 'AREA'
|
115
|
+
|
116
|
+
@md5 = Digest::MD5.hexdigest(@id.to_s)
|
117
|
+
|
118
|
+
@adjust = @parsed_meta.fetch(:adjust, '*')
|
119
|
+
@adjustval = @parsed_meta.fetch(:adjustval, '1')
|
120
|
+
@unit = @parsed_meta.fetch(:unit, '')
|
121
|
+
end
|
122
|
+
|
123
|
+
def to_hash
|
124
|
+
simple = {
|
125
|
+
mode: @mode, color: @color,
|
126
|
+
ulimit: @ulimit, llimit: @llimit,
|
127
|
+
type: @type,
|
128
|
+
adjust: @adjust, adjustval: @adjustval, unit: @unit,
|
129
|
+
complex: false,
|
130
|
+
md5: @md5, meta: @meta,
|
131
|
+
}
|
132
|
+
hash = super
|
133
|
+
hash.merge(simple)
|
134
|
+
end
|
135
|
+
|
136
|
+
def complex?
|
137
|
+
false
|
138
|
+
end
|
139
|
+
|
140
|
+
def update(args={})
|
141
|
+
meta = @parsed_meta.dup
|
142
|
+
args.each do |k, v|
|
143
|
+
case k.to_sym
|
144
|
+
when :number then @number = v
|
145
|
+
when :description then @description = v
|
146
|
+
when :sort then @sort = v
|
147
|
+
when :mode then @mode = v
|
148
|
+
when :color then @color = v
|
149
|
+
when :ulimit then @ulimit = v
|
150
|
+
when :llimit then @llimit = v
|
151
|
+
when :type then @type = v
|
152
|
+
else
|
153
|
+
meta[k.to_sym] = v
|
154
|
+
end
|
155
|
+
end
|
156
|
+
@parsed_meta = self.class.meta_clean(@parsed_meta.merge(meta))
|
157
|
+
@meta = @parsed_meta.to_json
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.meta_clean(args={})
|
161
|
+
args.delete_if do |k,v|
|
162
|
+
%w(id service_name section_name graph_name number
|
163
|
+
description sort mode color ulimit llimit type).include?(k.to_s)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class ComplexGraph < Graph
|
169
|
+
attr_accessor :sumup, :data_rows
|
170
|
+
attr_reader :complex_graph
|
171
|
+
|
172
|
+
def initialize(row)
|
173
|
+
super
|
174
|
+
|
175
|
+
uri = [:'type-1', :'path-1'].map{|k| @parsed_meta[k]}.join(':') + ':0' # stack
|
176
|
+
|
177
|
+
data_rows = []
|
178
|
+
|
179
|
+
first_row = {
|
180
|
+
type: @parsed_meta[:'type-1'],
|
181
|
+
path: @parsed_meta[:'path-1'].to_i,
|
182
|
+
stack: false,
|
183
|
+
graph_id: @parsed_meta[:'path-1'].to_i,
|
184
|
+
}
|
185
|
+
data_rows << first_row
|
186
|
+
|
187
|
+
unless @parsed_meta[:'type-2'].is_a?(Array)
|
188
|
+
[:'type-2', :'path-2', :'stack-2'].each do |key|
|
189
|
+
@parsed_meta[key] = [@parsed_meta[key]].flatten
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
@parsed_meta[:'type-2'].each_with_index do |type, i|
|
194
|
+
t = @parsed_meta[:'type-2'][i]
|
195
|
+
p = @parsed_meta[:'path-2'][i].to_i
|
196
|
+
s = @parsed_meta[:'stack-2'][i].is_a?(String) ? !!(@parsed_meta[:'stack-2'][i] =~ /^(1|true)$/i) : !!@parsed_meta[:'stack-2'][i]
|
197
|
+
uri += ':' + [t, p, (s ? '1' : '0')].join(':')
|
198
|
+
data_rows << {type: t, path: p, stack: s, graph_id: p}
|
199
|
+
end
|
200
|
+
|
201
|
+
@sumup = @parsed_meta.fetch(:sumup, '0') != '0' # '0' is false
|
202
|
+
@data_rows = data_rows
|
203
|
+
@complex_graph = uri
|
204
|
+
end
|
205
|
+
|
206
|
+
def to_hash
|
207
|
+
complex = {
|
208
|
+
sumup: @sumup, data: @data_rows,
|
209
|
+
complex: true
|
210
|
+
}
|
211
|
+
hash = super
|
212
|
+
hash.merge(complex)
|
213
|
+
end
|
214
|
+
|
215
|
+
def complex?
|
216
|
+
true
|
217
|
+
end
|
218
|
+
|
219
|
+
def update(args={})
|
220
|
+
meta = @parsed_meta.dup
|
221
|
+
args.each do |k, v|
|
222
|
+
case k.to_sym
|
223
|
+
when :number then @number = v
|
224
|
+
when :description then @description = v
|
225
|
+
when :sort then @sort = v
|
226
|
+
else
|
227
|
+
meta[k.to_sym] = v
|
228
|
+
end
|
229
|
+
end
|
230
|
+
@parsed_meta = self.class.meta_clean(@parsed_meta.merge(meta))
|
231
|
+
@meta = @parsed_meta.to_json
|
232
|
+
end
|
233
|
+
|
234
|
+
def self.meta_clean(args={})
|
235
|
+
args.delete_if do |k,v|
|
236
|
+
%w(id service_name section_name graph_name number description sort).include?(k.to_s)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|