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 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: