obf 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7c07d7e346399a237508f0f160653911ec761f06
4
- data.tar.gz: 68c07853e456950766b54f5c4a6d23c3a9b69541
3
+ metadata.gz: 6f9572d8127c5738b0d90c0856f8b7c1e501442a
4
+ data.tar.gz: 189a74ad02afcc68e05c34343280a6c8d92a2b7e
5
5
  SHA512:
6
- metadata.gz: a37ae6210514aba51e3144e70b0334c223770333a2d8a3057b65ffa20a199ed60c8cf7cb36e40bf4bed0ef9be5caba364eb295dff3c4d1b12235f122c9bae497
7
- data.tar.gz: 5c1dbae044dd81661223446c6c193b6ab15afa88da17c28787b5d97798b90dacde3b3a9c8e2262074ba563c5276aefe4dc0a42f1ea62d49b5d133cb5eb31b67d
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'] = 'open-board-0.1'
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' => 'open-board-0.1',
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
@@ -1,4 +1,6 @@
1
1
  module OBF::OBF
2
+ FORMAT="open-board-0.1"
3
+ FORMAT_CURRENT_VERSION=0.1
2
4
  def self.to_external(obf, opts)
3
5
  OBF::External.from_obf(obf, opts)
4
6
  end
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
@@ -12,6 +12,7 @@ module OBF
12
12
  require 'obf/pdf'
13
13
  require 'obf/png'
14
14
  require 'obf/utils'
15
+ require 'obf/validator'
15
16
 
16
17
  require 'obf/picto4me'
17
18
  require 'obf/avz'
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.2
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-03 00:00:00.000000000 Z
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: