obf 0.4.2 → 0.5.0
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 +4 -4
- data/lib/obf/external.rb +4 -2
- data/lib/obf/obf.rb +2 -0
- data/lib/obf/utils.rb +2 -2
- data/lib/obf/validator.rb +307 -0
- data/lib/obf.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f9572d8127c5738b0d90c0856f8b7c1e501442a
|
4
|
+
data.tar.gz: 189a74ad02afcc68e05c34343280a6c8d92a2b7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 29bcaa08c58c37653494ae993343982bb06421d9f505eed6d445d8f00b7265011ed7fc1c5efda6c6bcc1afc25d7b768b96bdcc3526482f8e7b52f33be72c7efd
|
7
|
+
data.tar.gz: 2d7e74209119cfa1f1da0afac1e0a0ad96a91a36b413c277a33ef73dcdf42260f0ae93c3ca0f8ee7f42ecaf18ae2c383bd82bf5c25b16b65c1c1843e88387dc2
|
data/lib/obf/external.rb
CHANGED
@@ -11,7 +11,7 @@ module OBF::External
|
|
11
11
|
res = OBF::Utils.obf_shell
|
12
12
|
res['id'] = hash['id']
|
13
13
|
res['locale'] = hash['locale'] || 'en'
|
14
|
-
res['format'] =
|
14
|
+
res['format'] = OBF::OBF::FORMAT
|
15
15
|
res['name'] = hash['name']
|
16
16
|
res['default_layout'] = hash['default_layout'] || 'landscape'
|
17
17
|
res['url'] = hash['url']
|
@@ -221,7 +221,7 @@ module OBF::External
|
|
221
221
|
end
|
222
222
|
end
|
223
223
|
manifest = {
|
224
|
-
'format' =>
|
224
|
+
'format' => OBF::OBF::FORMAT,
|
225
225
|
'root' => paths['boards'][root_board['id']]['path'],
|
226
226
|
'paths' => {}
|
227
227
|
}
|
@@ -318,4 +318,6 @@ module OBF::External
|
|
318
318
|
File.unlink(tmp_path) if File.exist?(tmp_path)
|
319
319
|
dest_path
|
320
320
|
end
|
321
|
+
|
322
|
+
class StructureError < StandardError; end
|
321
323
|
end
|
data/lib/obf/obf.rb
CHANGED
data/lib/obf/utils.rb
CHANGED
@@ -319,13 +319,13 @@ module OBF::Utils
|
|
319
319
|
end
|
320
320
|
|
321
321
|
def self.update_current_progress(*args)
|
322
|
-
if Object.const_defined?('Progress')
|
322
|
+
if Object.const_defined?('Progress') && Progress.respond_to?(:update_current_progress)
|
323
323
|
Progress.update_current_progress(*args)
|
324
324
|
end
|
325
325
|
end
|
326
326
|
|
327
327
|
def self.as_progress_percent(a, b, &block)
|
328
|
-
if Object.const_defined?('Progress')
|
328
|
+
if Object.const_defined?('Progress') && Progress.respond_to?(:as_percent)
|
329
329
|
Progress.as_percent(a, b, &block)
|
330
330
|
else
|
331
331
|
block.call
|
@@ -0,0 +1,307 @@
|
|
1
|
+
module OBF
|
2
|
+
class Validator
|
3
|
+
def add_check(type, description, &block)
|
4
|
+
return if @blocked
|
5
|
+
@checks << {
|
6
|
+
'type' => type,
|
7
|
+
'description' => description,
|
8
|
+
'valid' => true
|
9
|
+
}
|
10
|
+
begin
|
11
|
+
block.call
|
12
|
+
rescue ValidationError => e
|
13
|
+
@errored = true
|
14
|
+
@checks[-1]['valid'] = false
|
15
|
+
@checks[-1]['error'] = e.message
|
16
|
+
@blocked = true if e.blocker
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def err(message, blocker=false)
|
21
|
+
raise ValidationError.new(blocker), message
|
22
|
+
end
|
23
|
+
|
24
|
+
def warn(message)
|
25
|
+
@warned = true
|
26
|
+
@checks[-1]['warnings'] ||= []
|
27
|
+
@checks[-1]['warnings'] << message
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.validate_obf(path)
|
31
|
+
v = self.new
|
32
|
+
v.validate_obf(path)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.validate_obz(path)
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_obf(path, opts={})
|
39
|
+
@blocked = nil
|
40
|
+
@errored = false
|
41
|
+
@checks = []
|
42
|
+
|
43
|
+
# TODO enforce extra attributes being defined with ext_
|
44
|
+
|
45
|
+
json = nil
|
46
|
+
add_check('valid_json', "Valid JSON File") do
|
47
|
+
begin
|
48
|
+
json = JSON.parse(File.read(path))
|
49
|
+
rescue => e
|
50
|
+
err "Couldn't parse as JSON", true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
ext = nil
|
55
|
+
add_check('to_external', "Valid OBF Structure") do
|
56
|
+
begin
|
57
|
+
ext = External.from_obf(path, {})
|
58
|
+
rescue External::StructureError => e
|
59
|
+
err "Couldn't parse structure: #{e.message}", true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
add_check('format_version', "Valid Format Version") do
|
64
|
+
if !ext['format']
|
65
|
+
err "format attribute is required, set to #{FORMAT}"
|
66
|
+
end
|
67
|
+
version = ext['format'].split(/-/, 3)[-1].to_f
|
68
|
+
if version > OBF::FORMAT_CURRENT_VERSION
|
69
|
+
err "format version (#{version}) is invalid, current version is #{OBF::FORMAT_CURRENT_VERSION}"
|
70
|
+
elsif version < OBF::FORMAT_CURRENT_VERSION
|
71
|
+
warn "format version (#{version}) is old, consider updating to #{OBF::FORMAT_CURRENT_VERSION}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
add_check('id', "Valid Board ID") do
|
76
|
+
if !ext['id']
|
77
|
+
err "id attribute is required"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
add_check('locale', "Valid Locale") do
|
82
|
+
if !ext['locale']
|
83
|
+
err "locale attribute is required, please set to \"en\" for English"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
add_check('extras', "Extra Attributes") do
|
88
|
+
attrs = ['format', 'id', 'locale', 'url', 'data_url', 'name', 'description_html', 'buttons', 'images', 'sounds', 'grid', 'license']
|
89
|
+
ext.keys.each do |key|
|
90
|
+
if !attrs.include?(key) && !key.match(/^ext_/)
|
91
|
+
warn "#{key} attribute is not defined in the spec, should be prefixed with ext_yourapp_"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
add_check('description', "Description Attributes") do
|
97
|
+
if !ext['name']
|
98
|
+
warn "name attribute is strongly recommended"
|
99
|
+
end
|
100
|
+
if !ext['description_html']
|
101
|
+
warn "description_html attribute is recommended"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
add_check('buttons', "Valid buttons attribute") do
|
106
|
+
if !ext['buttons']
|
107
|
+
err "buttons attribute is required"
|
108
|
+
elsif !ext['buttons'].is_a?(Array)
|
109
|
+
err "buttons attribute must be an array"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
add_check('grid', "Valid grid attribute") do
|
114
|
+
if !ext['grid']
|
115
|
+
err "grid attribute is required"
|
116
|
+
elsif !ext['grid'].is_a?(Hash)
|
117
|
+
err "grid attribute must be a hash"
|
118
|
+
elsif !ext['grid']['rows'].is_a?(Fixnum) || ext['grid']['rows'] < 1
|
119
|
+
err "grid.row attribute must be a valid positive number"
|
120
|
+
elsif !ext['grid']['columns'].is_a?(Fixnum) || ext['grid']['columns'] < 1
|
121
|
+
err "grid.column attribute must be a valid positive number"
|
122
|
+
end
|
123
|
+
if ext['grid']['rows'] > 20
|
124
|
+
warn "grid.row (#{ext['grid']['rows']}) is probably too large a number for most systems"
|
125
|
+
end
|
126
|
+
if ext['grid']['columns'] > 20
|
127
|
+
warn "grid.column (#{ext['grid']['columns']}) is probably too large a number for most systems"
|
128
|
+
end
|
129
|
+
if !ext['grid']['order']
|
130
|
+
err "grid.order is required"
|
131
|
+
elsif !ext['grid']['order'].is_a?(Array)
|
132
|
+
err "grid.order must be an array of arrays"
|
133
|
+
elsif ext['grid']['order'].length != ext['grid']['rows']
|
134
|
+
err "grid.order length (#{ext['grid']['order'].length}) must match grid.rows (#{ext['grid']['rows']})"
|
135
|
+
elsif !ext['grid']['order'].all?{|r| r.is_a?(Array) && r.length == ext['grid']['columns'] }
|
136
|
+
err "grid.order must contain #{ext['grid']['rows']} arrays each of size #{ext['grid']['columns']}"
|
137
|
+
end
|
138
|
+
|
139
|
+
attrs = ['rows', 'columns', 'order']
|
140
|
+
ext['grid'].keys.each do |key|
|
141
|
+
if !attrs.include?(key) && !key.match(/^ext_/)
|
142
|
+
warn "grid.#{key} attribute is not defined in the spec, should be prefixed with ext_yourapp_"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
add_check('grid_ids', "Valid Button IDs in grid.order attribute") do
|
148
|
+
button_ids = []
|
149
|
+
if ext['buttons'] && ext['buttons'].is_a?(Array)
|
150
|
+
ext['buttons'].each{|b| button_ids << b['id'] if b.is_a?(Hash) && b['id'] }
|
151
|
+
end
|
152
|
+
used_button_ids = []
|
153
|
+
if ext['grid'] && ext['grid']['order'] && ext['grid']['order'].is_a?(Array)
|
154
|
+
ext['grid']['order'].each do |row|
|
155
|
+
if row.is_a?(Array)
|
156
|
+
row.each do |id|
|
157
|
+
if id
|
158
|
+
used_button_ids << id
|
159
|
+
if !button_ids.include?(id)
|
160
|
+
err "grid.order references button with id #{id} but no button with that id found in buttons attribute"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
warn("board has no buttons defined in the grid") if used_button_ids.length == 0
|
168
|
+
warn("not all defined buttons were included in the grid order") if (button_ids = used_button_ids).length > 0
|
169
|
+
end
|
170
|
+
|
171
|
+
unless opts['obz']
|
172
|
+
button_image_ids = []
|
173
|
+
if ext['buttons'] && ext['buttons'].is_a?(Array)
|
174
|
+
ext['buttons'].each{|b| button_image_ids << b['image_id'] if b.is_a?(Hash) && b['image_id'] }
|
175
|
+
end
|
176
|
+
add_check('images', "Valid images attribute") do
|
177
|
+
|
178
|
+
if !ext['images']
|
179
|
+
err "images attribute is required"
|
180
|
+
elsif !ext['images'].is_a?(Array)
|
181
|
+
err "images attribute must be an array"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
if ext['images'] && ext['images'].is_a?(Array)
|
186
|
+
ext['images'].each_with_index do |image, idx|
|
187
|
+
add_check("image[#{idx}]", "Valid image at [#{idx}]") do
|
188
|
+
if !image.is_a?(Hash)
|
189
|
+
err "image must be a hash"
|
190
|
+
elsif !image['id']
|
191
|
+
err "image.id is required"
|
192
|
+
elsif !image['width'] || !image['width'].is_a?(Fixnum)
|
193
|
+
err "image.width must be a valid positive number"
|
194
|
+
elsif !image['height'] || !image['height'].is_a?(Fixnum)
|
195
|
+
err "image.height must be a valid positive number"
|
196
|
+
elsif !image['content_type'] || !image['content_type'].match(/^image\/.+$/)
|
197
|
+
err "image.content_type must be a valid image mime type"
|
198
|
+
elsif !image['url'] && !image['data'] && !image['symbol']
|
199
|
+
err "image must have data, url or symbol attribute defined"
|
200
|
+
elsif image['data'] && !image['data'].match(/^data:image\/.+;base64,.+$/)
|
201
|
+
err "image.data must be a valid data URI if defined"
|
202
|
+
elsif image['symbol'] && !image['symbol'].is_a?(Hash)
|
203
|
+
err "image.symbol must be a hash if defined"
|
204
|
+
end
|
205
|
+
|
206
|
+
attrs = ['id', 'width', 'height', 'content_type', 'data', 'url', 'symbol', 'path', 'data_url', 'license']
|
207
|
+
image.keys.each do |key|
|
208
|
+
if !attrs.include?(key) && !key.match(/^ext_/)
|
209
|
+
warn "image.#{key} attribute is not defined in the spec, should be prefixed with ext_yourapp_"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
add_check('sounds', "Valid sounds attribute") do
|
217
|
+
|
218
|
+
if !ext['sounds']
|
219
|
+
err "sounds attribute is required"
|
220
|
+
elsif !ext['sounds'].is_a?(Array)
|
221
|
+
err "sounds attribute must be an array"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
if ext['sounds'] && ext['sounds'].is_a?(Array)
|
226
|
+
ext['sounds'].each_with_index do |sound, idx|
|
227
|
+
add_check("sounds[#{idx}]", "Valid sound at [#{idx}]") do
|
228
|
+
if !sound.is_a?(Hash)
|
229
|
+
err "sound must be a hash"
|
230
|
+
elsif !sound['id']
|
231
|
+
err "sound.id is required"
|
232
|
+
elsif !sound['duration'] || !sound['duration'].is_a?(Fixnum)
|
233
|
+
err "sound.duration must be a valid positive number"
|
234
|
+
elsif !sound['content_type'] || !sound['content_type'].match(/^audio\/.+$/)
|
235
|
+
err "sound.content_type must be a valid audio mime type"
|
236
|
+
elsif !sound['url'] && !sound['data'] && !sound['symbol']
|
237
|
+
err "sound must have data, url or symbol attribute defined"
|
238
|
+
elsif sound['data'] && !sound['data'].match(/^data:audio\/.+;base64,.+$/)
|
239
|
+
err "sound.data must be a valid data URI if defined"
|
240
|
+
end
|
241
|
+
|
242
|
+
attrs = ['id', 'duration', 'content_type', 'data', 'url', 'path', 'data_url', 'license']
|
243
|
+
sound.keys.each do |key|
|
244
|
+
if !attrs.include?(key) && !key.match(/^ext_/)
|
245
|
+
warn "sound.#{key} attribute is not defined in the spec, should be prefixed with ext_yourapp_"
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
if ext['buttons'] && ext['buttons'].is_a?(Array)
|
254
|
+
ext['buttons'].each_with_index do |button, idx|
|
255
|
+
add_check("buttons[#{idx}]", "Valid button at [#{idx}]") do
|
256
|
+
if !button.is_a?(Hash)
|
257
|
+
err "button must be a hash"
|
258
|
+
elsif !button['id']
|
259
|
+
err "button.id is required"
|
260
|
+
elsif !button['label']
|
261
|
+
err "button.label is required"
|
262
|
+
end
|
263
|
+
['top', 'left', 'width', 'height'].each do |attr|
|
264
|
+
if button[attr] && ((!button[attr].is_a?(Fixnum) && !button[attr].is_a?(Float)) || button[attr] < 0)
|
265
|
+
warn "button.#{attr} should be a positive number"
|
266
|
+
end
|
267
|
+
end
|
268
|
+
['background_color', 'border_color'].each do |color|
|
269
|
+
if button[color]
|
270
|
+
if !button[color].match(/^\s*rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(,\s*[01]\.?\d*)?\)\s*/)
|
271
|
+
err "button.#{color} must be a valid rgb or rgba value if defined (\"#{button[color]}\" is invalid)"
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
if button['hidden'] != nil && button['hidden'] != true && button['hidden'] != false
|
276
|
+
err "button.hidden must be a boolean if defined"
|
277
|
+
end
|
278
|
+
if !button['image_id']
|
279
|
+
warn "button.image_id is recommended"
|
280
|
+
end
|
281
|
+
if button['action'] && !button['action'].match(/^(:|\+)/)
|
282
|
+
err "button.action must start with either : or + if defined"
|
283
|
+
end
|
284
|
+
|
285
|
+
attrs = ['id', 'label', 'vocalization', 'image_id', 'hidden', 'background_color', 'border_color', 'action', 'load_board', 'top', 'left', 'width', 'height']
|
286
|
+
button.keys.each do |key|
|
287
|
+
if !attrs.include?(key) && !key.match(/^ext_/)
|
288
|
+
warn "button.#{key} attribute is not defined in the spec, should be prefixed with ext_yourapp_"
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
return @checks
|
297
|
+
end
|
298
|
+
|
299
|
+
class ValidationWarning < StandardError; end
|
300
|
+
class ValidationError < StandardError
|
301
|
+
attr_reader :blocker
|
302
|
+
def initialize(blocker=false)
|
303
|
+
@blocker = blocker
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
data/lib/obf.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: obf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Whitmer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -142,6 +142,7 @@ files:
|
|
142
142
|
- lib/obf/sfy.rb
|
143
143
|
- lib/obf/unknown_file.rb
|
144
144
|
- lib/obf/utils.rb
|
145
|
+
- lib/obf/validator.rb
|
145
146
|
- lib/tinycolor_convert.js
|
146
147
|
homepage: https://github.com/CoughDrop/obf
|
147
148
|
licenses:
|