focuslight 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|