Class: Ballonizer

Inherits:
Object
  • Object
show all
Defined in:
lib/ballonizer.rb

Overview

This gem provides mechanisms to allow ballons (or speech bubbles) to be added/removed/edited over images of a HTML or XHTML document and to be persisted. The edition of the ballons is possible by the javascript module provided by this gem. The persistence is allowed by the Ballonizer class. The Ballonizer class is basically a wrapper around the database used to persist the ballons, and offer methods to process the requests made by the client side (by a form created by the javascript module), and to modify a (X)HTML document adding the ballons of the image over it.

This class lacks a lot of features like: access to an abstraction of the ballons, images and their relationship; control over users who edit the ballons; access to the old versions of the ballon set of a image (that are stored in the database, but only can be accessed directly by the Sequel::Database object). It’s a work in progress, be warned to use carefully and motivated to contribute.

The JavaScript library used to allow edition in the client side works as follows: double click over the image add a ballon, double click over a ballon allow edit the text, when the ballon lose the focus it returns to the non-edition state, a ballon without text (or only with spaces) it’s automatically removed when lose focus, drag the ballon change its position (restricted to image space), drag ballon by the right-bottom handle resize the ballon (also restricted to image space). Any change in the ballons make visible a button fixed in the right-top corner of the browser viewport. Every time a ballons is changed (or added/removed) the json of a hidden form is updated. The button submits this json by POST request to the url configured by :form_handler_url setting.

To the image be ‘ballonized’ it have to match the :img_to_ballonize_css_selector. The ‘ballonized’ term here means: have the ballons added over the image in ballonize_page.

To use this class with your (rack isn’t?) app you need to: create the necessary tables in a Sequel::Database object with Ballonizer.create_tables; create a ballonizer instance with the url where you gonna handle the ballon change requests and where provide the assets. Handle the ballon changes request in that url with process_submit. Call instance.ballonize_page over the html documents that can have the images to be ballonized. Check if the image match the css selector :img_to_ballonize_css_selector.

What’s explained above is basically the example you can access with ‘rake example’ and is in the examples/ballonizer_app/config.ru file. You can reset the database with ‘rake db:reset’ (and if you pass an argument as ‘rake db:reset’ you can create the tables in the database already used by your app). The tables names are: images, ballons, ballonized_image_versions, ballonized_image_ballons.

Changelog:

v0.5.1:
 * js_load_snippet can take a settings arg too. Fixed ballonize_page to
   use the :form_handler_url from the settings argument.
v0.5.0:
 * The *_html_links methods can take a settings argument.
 * Fixed bug where passing a new asset path to the ballonize_page don't
   settings parameter change the asset path that it uses.
 * Asset path settings now are parsed as real URIs (need to have a
   trailing slash if the intent is use as a dir).
 * Updated the rspec version used by the gem (fixed deprecation).
v0.4.0: 
 * Changed the way the Javascript module add containers in the page
   to avoid creating invalid HTML4.0.1/XHTML1.1/HTML5 documents.
 * Now the ballonize_page takes a mime-type argument to decide if
   the page has to be parsed as XML or HTML (trying to be in
   conformance with http://www.w3.org/TR/xhtml-media-types/).
 * The change in the ballon size now change the font-size of the
   ballon text.
 * Database schema change, as consequence of the font-size change,
   the database now stores the font-size. No migration provided for
   databases in the old format, but the font-size field can be null.
   The migration only require adding this column with null value to
   all records (see the create_tables code).
 * Fixed a bug in the Javascript module that give wrong position and
   size values to all ballons that aren't edited/added before submmiting
   (only if the image wasn't loaded before the javascript loading).

Author:

  • Henrique Becker

Defined Under Namespace

Classes: Error, SubmitError

Constant Summary collapse

ASSETS =

The load paths of assets inside the gem and the files inside each path, in the order they need to be included (the files of the first path need to be included before the files in the second path, and the files in the same path need to be included in the specified order). Give preference to the asset(s)_* and *_html_links methods over this constant.

Workaround.deep_freeze([
  ['vendor/assets/javascripts', [
    'jquery-2.0.1.js',
    'jquery.json-2.4.min.js',
    'jquery-ui-1.10.3.custom.min.js']],
  ['lib/assets/javascripts', [
    'ballonizer.js']],
  ['vendor/assets/stylesheets', [
    'ui-lightness/jquery-ui-1.10.3.custom.min.css']],
  ['lib/assets/stylesheets', [
    'ballonizer.css']]
])
DEFAULT_SETTINGS =

The default #settings

{
  # The css selector used to define the elements to ballonize.
  img_to_ballonize_css_selector: 'img.to_ballonize',
  # A url to be used in the client-side action attribute of the form for
  # ballon submition. The value will be used in the javascript snippet that
  # initialize the ballonizer client javascript allowing ballon edition
  # (and consequently creating the form).
  form_handler_url: '#',
  # Define if the javascript code that allow edition will be added to the page.
  # (this don't refer to the jquery-* libs and the ballonizer.js only the
  # snippet to execute when the page is ready)
  add_js_for_edition: true,
  # A path string to prefix each href of the css stylesheet links generated
  # by the js_libs_html_links, and, possibly, added by the ballonize_page
  # object. Example: if you use Ballonizer.assets_app mapped to '/assets'
  # then use '/assets' here. This is used with the :add_required_css setting.
  css_asset_path_for_link: nil,
  # If the ballonize_page method will add or not the html generated by
  # #css_html_links (require the :css_asset_path_for_link to be defined).
  add_required_css: false,
  # A path string to prefix each js source src generated by the
  # object. Example: if you use Ballonizer.assets_app mapped to '/assets'
  # then use '/assets' here. This is used with the
  # :add_required_js_libs_for_edition setting.
  js_asset_path_for_link: nil,
  # If the ballonize_page method will add or not the html generated by
  # #js_libs_html_links (require the :js_asset_path_for_link to be defined).
  add_required_js_libs_for_edition: false,
  # If true and the database argument don't have any of the tables used by
  # the class call create_tables over the database argument. If false or the
  # database has at leat one of the tables does nothing.
  create_tables_if_none: false
}.freeze.each { | _, v| v.freeze }

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(db, settings = {}) ⇒ Ballonizer

Create a new Ballonizer object from a Sequel Database (with the expected tables, that can be created with Ballonizer.create_tables) and a optional hash of settings.

Parameters:

  • db (String, Sequel::Database)

    A Sequel::Database or a String to be used with Sequel::Database.connect. Is necessary to create the tables with Ballonizer.create_tables unless you have set the :create_table_if_none setting to true.

  • settings (Hash{Symbol => String}) (defaults to: {})

    A optional hash of settings. The default value and explanation of each option are documented in the DEFAULT_SETTINGS constant.

See Also:



221
222
223
224
225
226
227
228
229
230
231
# File 'lib/ballonizer.rb', line 221

def initialize(db, settings = {})
  @settings = DEFAULT_SETTINGS.merge(settings)
  if db.is_a? String
    db = Sequel::Database.connect(db)
  end
  if @settings[:create_tables_if_none] &&
       ! (self.class.used_tables.any? { | name | db.table_exists? name })
    self.class.create_tables(db)
  end
  @db = db
end

Instance Attribute Details

#dbObject

Returns the value of attribute db.



93
94
95
# File 'lib/ballonizer.rb', line 93

def db
  @db
end

#settingsObject

Returns the value of attribute settings.



93
94
95
# File 'lib/ballonizer.rb', line 93

def settings
  @settings
end

Class Method Details

.asset_absolute_pathsArray<String>

List of absolute filepaths to the css and js files needed by the client counterpart and provided by the gem. To all who not want to use assets_app.

Returns:

  • (Array<String>)

    A frozen array of frozen strings.

See Also:



629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
# File 'lib/ballonizer.rb', line 629

def self.asset_absolute_paths
  return @asset_absolute_paths if @asset_absolute_paths

  absolute_lib_dir = File.dirname(File.realpath(__FILE__))
  ballonizer_gem_root_dir = File.expand_path('../', absolute_lib_dir)

  @asset_absolute_paths = ASSETS.map do | load_path_and_files |
    relative_load_path, filepaths = *load_path_and_files
    absolute_load_path = File.expand_path(relative_load_path, ballonizer_gem_root_dir)

    filepaths.map do | filepath  |
      File.expand_path(filepath, absolute_load_path)
    end
  end

  @asset_absolute_paths.flatten!
  @asset_absolute_paths.freeze
end

.asset_load_pathsArray<String>

List of paths (relative to the gem root directory) to the directories with the css and js provided by the gem.

Returns:

  • (Array<String>)

    A frozen array of frozen strings.



596
597
598
599
600
601
602
603
604
605
606
607
608
609
# File 'lib/ballonizer.rb', line 596

def self.asset_load_paths
  return @asset_load_paths if @asset_load_paths

  absolute_lib_dir = File.dirname(File.realpath(__FILE__))
  ballonizer_gem_root_dir = File.expand_path('../', absolute_lib_dir)

  @asset_load_paths = ASSETS.map do | load_path_and_files |
    load_path = load_path_and_files.first
    File.expand_path(load_path, ballonizer_gem_root_dir)
  end

  @asset_load_paths.flatten!
  @asset_load_paths.freeze
end

.asset_logical_pathsArray<String>

List of logical paths to the css and js assets. The assets_app respond to any requisition to one of these paths.

Returns:

  • (Array<String>)

    A frozen array of frozen strings.



614
615
616
617
618
619
620
621
622
623
# File 'lib/ballonizer.rb', line 614

def self.asset_logical_paths
  return @asset_logical_paths if @asset_logical_paths

  @asset_logical_paths = ASSETS.map do | load_path_and_files |
    load_path_and_files.last
  end

  @asset_logical_paths.flatten!
  @asset_logical_paths.freeze
end

.assets_appSprockets::Environment

A Rack app that provide the gem css and js. Each call to this method return a new object (clone). The Sprockets::Environment isn’t frozen because it can’t be used with ‘run’ in a rack app if frozen.

Returns:

  • (Sprockets::Environment)

See Also:



653
654
655
656
657
658
659
660
661
# File 'lib/ballonizer.rb', line 653

def self.assets_app
  # dont freeze because run don't work in a frozen sprockets env
  return @assets_app.clone if @assets_app
  @assets_app = Sprockets::Environment.new
  asset_load_paths.each do | load_path |
    @assets_app.prepend_path load_path
  end 
  @assets_app.clone
end

.create_ballon_node(ballon_data) ⇒ Object



459
460
461
462
463
464
465
466
467
468
469
470
# File 'lib/ballonizer.rb', line 459

def self.create_ballon_node(ballon_data)
  text = HTMLEntities.new.encode(ballon_data[:text])

  style = ''
  [:top, :left, :width, :height].each do | sym |
    # transform ratio [0,1] to percent [0, 100]
    style = style + "#{sym}: #{(ballon_data[sym] * 100)}%;"
  end
  style = style + "font-size: #{ballon_data[:font_size]}px;"

  "<span class='ballonizer_ballon' style='#{style}'>#{text}</span>"
end

.create_tables(db) ⇒ void

This method returns an undefined value.

Executes the create_table operations over the Sequel::Database argument.

Parameters:

  • db (Sequel::Database)

    The database where create the tables.



514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
# File 'lib/ballonizer.rb', line 514

def self.create_tables(db)
  db.create_table(:images) do
    primary_key :id
    String :img_src, :size => 255, :unique => true, :allow_null => false
  end
  db.create_table(:ballons) do
    primary_key :id

    String :text, :size => 255, :allow_null => false
    Float :top, :allow_null => false
    Float :left, :allow_null => false
    Float :width, :allow_null => false
    Float :height, :allow_null => false
    # the font_size allow null to support databases migrated from old versions
    # (that don't have this field)
    Float :font_size, :allow_null => true
  end
  db.create_table(:ballonized_image_versions) do
    Integer :version
    foreign_key :image_id, :images
    DateTime :time, :allow_null => false
    primary_key [:version, :image_id]
  end
  db.create_table(:ballonized_image_ballons) do
    Integer :version
    foreign_key :image_id, :images
    foreign_key :ballon_id, :ballons
    foreign_key [:version, :image_id], :ballonized_image_versions
  end
end

.used_tablesArray<Symbol>

The names (as symbols) of the tables used by instances of the class.

Returns:

  • (Array<Symbol>)

    An frozen array of symbols



205
206
207
# File 'lib/ballonizer.rb', line 205

def self.used_tables
  USED_TABLES
end

Instance Method Details

#ballonize_page(page, page_url, mime_type, settings = {}) ⇒ String

Wrap each image to ballonize with a container, add its ballons to the container and, possibly, add the css and js libs and snippet for the edition initialization. Don’t make any change if the page has no images to ballonize. If the page can’t be parsed (as HTML or X(HT)ML, depending of the mime-type) return the page argument without throwing any exceptions. Throw an exception if the mime-type doesn’t match with html or xhtml.

Parameters:

  • page (String)

    The (X)HTML page.

  • page_url (String)

    The url of the page to be ballonized, necessary to make absolute the src attribute of img (if it’s relative).

  • settings (Hash{Symbol => String}) (defaults to: {})

    Optional. Hash to be merged with the instance #settings (this argument override the #settings ones).

  • mime_type

    A string that have the substring ‘text/html’ or ‘application/xhtml+xml’.

Returns:

  • (String)

    The ballonized page (new string), or the same string, if the parse has failed.

Raises:

  • (Ballonizer::Error)

    If the mime-type don’t match either ‘text/html’ or ‘application/xhtml+xml’.



416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/ballonizer.rb', line 416

def ballonize_page(page, page_url, mime_type, settings = {})
  settings = @settings.merge(settings)

  # can raise Ballonizer::Error if the mime-type is invalid
  parsed_page = Workaround.parse_html_or_xhtml(page, mime_type)
  # if can't parse return the page unaltered
  if parsed_page.nil?
    return page
  end

  selector = settings[:img_to_ballonize_css_selector]
  imgs = parsed_page.css(selector)

  unless imgs.empty?
    imgs.wrap('<span class="ballonizer_image_container" ></span>')

    imgs.each do | img |
      img_src = img['src']
      absolute_normal_src = Addressable::URI.parse(page_url)
                                            .join(img_src)
                                            .normalize.to_s
      ballons = last_ballon_set_of_image(absolute_normal_src)
      ballons.each do | ballon |
        img.add_previous_sibling(self.class.create_ballon_node(ballon))
      end
    end
    
    head = parsed_page.at_css('head')
    if settings[:add_required_css]
      head.children.last.add_next_sibling(css_html_links(settings))
    end
    if settings[:add_required_js_libs_for_edition]
      head.children.last.add_next_sibling(js_libs_html_links(settings))
    end
    if settings[:add_js_for_edition]
      head.children.last.add_next_sibling(js_load_snippet(settings))
    end
  end
  
  parsed_page.to_s
end

The (X)HTML fragment with the link tags that are added to the page by ballonize_page if the :add_required_css setting is true (the default is false).

Parameters:

  • settings (Hash{Symbol => String}) (defaults to: {})

    Optional. Hash to be merged with the instance #settings (this argument override the #settings ones).

Returns:

  • (String, NilClass)

    A String when the :css_asset_path_for_link is defined, nil otherwise.



552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
# File 'lib/ballonizer.rb', line 552

def css_html_links(settings = {})
  settings = @settings.merge(settings)
  return nil unless settings[:css_asset_path_for_link]

  link_template = '<link rel="stylesheet" type="text/css" href="PATH" />'
  css_paths = self.class.asset_logical_paths.select do | p |
    /^.+\.css$/.match(p)
  end

  links = css_paths.map do | p |
    p = Workaround.join_uris(settings[:css_asset_path_for_link], p)
    link_template.sub('PATH', p)
  end

  links.join('')
end

The (X)HTML fragment with the script tags that are added to the page by ballonize_page if the :add_required_js_libs_for_edition setting is true (the default is false).

Parameters:

  • settings (Hash{Symbol => String}) (defaults to: {})

    Optional. Hash to be merged with the instance #settings (this argument override the #settings ones).

Returns:

  • (String, NilClass)

    A String when the :js_asset_path_for_link is defined, nil otherwise.



576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
# File 'lib/ballonizer.rb', line 576

def js_libs_html_links(settings = {})
  settings = self.settings.merge(settings)
  return nil unless settings[:js_asset_path_for_link]

  link_template = '<script type="text/javascript" src="PATH" ></script>'
  js_libs_paths = self.class.asset_logical_paths.select do | p |
    /^.+\.js$/.match(p)
  end

  links = js_libs_paths.map do | p |
    p = Workaround.join_uris(settings[:js_asset_path_for_link], p)
    link_template.sub('PATH', p)
  end

  links.join('')
end

#js_load_snippet(settings = {}) ⇒ String

Return a String with the snippet added to the pages to allow edition in them.

Parameters:

  • settings (Hash{Symbol => String}) (defaults to: {})

    Optional. Hash to be merged with the instance #settings (this argument override the #settings ones).

Returns:

  • (String)

    The added snippet. Already with the <script/> tag around it.



498
499
500
501
502
503
504
505
506
507
508
509
# File 'lib/ballonizer.rb', line 498

def js_load_snippet(settings = {})
  settings = @settings.merge(settings)
  "    <script type=\"text/javascript\">\n      $(document).ready(function() {\n        Ballonizer('\#{settings[:form_handler_url]}',\n                   '.ballonizer_image_container',\n                   $('body'));\n      })\n    </script>\n  EOF\nend\n"

#last_ballon_set_of_image(img_src) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

This method don’t make distinction between a image in the database without any ballons (removed in the last version, by example) or a image that isn’t in the database (both return a empty array).

Don’t use this method. It is for internal use only.



477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
# File 'lib/ballonizer.rb', line 477

def last_ballon_set_of_image(img_src)
  db_image = self.db[:images].first({img_src: img_src})
  if db_image
    image_id = db_image[:id]
    version = self.db[:ballonized_image_versions].where({image_id: image_id})
                                                 .max(:version)
    self.db[:ballonized_image_ballons]
        .join(:ballons, { ballonized_image_ballons__version: version,
                          ballonized_image_ballons__image_id: image_id,
                          ballonized_image_ballons__ballon_id: :ballons__id
                        }).select(:text, :top, :left, :width, :height,
                                  :font_size).all
  else
    []
  end
end

#process_submit(env, time = nil) ⇒ Ballonizer

Convenience method for process_submit_json, extract the json from the request, validate and pass to the method.

Parameters:

  • env

    A env Rack hash.

Returns:

Raises:

See Also:



239
240
241
242
243
244
# File 'lib/ballonizer.rb', line 239

def process_submit(env, time = nil)
  request = Rack::Request.new(env)
  submit_json = request['ballonizer_data']
  valid_submit_json?(submit_json, true)
  process_submit_json(submit_json, time)
end

#process_submit_hash(submit_hash, time = nil) ⇒ Ballonizer

Behave as process_submit_json except that takes a already parsed json (hash) and don’t check if it’s tainted.

Parameters:

  • submit_hash (Hash)

    A JSON hash. Validate with #valid_submit_json?.

  • time (Time) (defaults to: nil)

    A Time instance to be used in place of Time.now. Optional.

Returns:



359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/ballonizer.rb', line 359

def process_submit_hash(submit_hash, time = nil)
  time = Time.now unless time
  self.db.transaction do
    images = self.db[:images]
    db_ballons = self.db[:ballons]
    ballonized_image_versions = self.db[:ballonized_image_versions]
    ballonized_image_ballons = self.db[:ballonized_image_ballons]

    submit_hash.each do | img_src, ballons |
      img_src = Addressable::URI.parse(img_src).normalize.to_s
      db_image = images.first({img_src: img_src})
      image_id, version = nil, nil

      if db_image
        image_id = db_image[:id]
        version = ballonized_image_versions.where({image_id: image_id})
                                            .max(:version) + 1
      else
        image_id = images.insert({img_src: img_src})
        version = 1
      end

      ballonized_image_versions.insert({
        image_id: image_id,
        version: version,
        time: time
      })
      ballons.each do | ballon |
        db_ballon = db_ballons.first(ballon)
        ballon_id = db_ballon ? db_ballon[:id] : db_ballons.insert(ballon)
        ballonized_image_ballons.insert({
          image_id: image_id,
          version: version,
          ballon_id: ballon_id,
        })
      end
    end
  end
end

#process_submit_json(submit_json, time = nil) ⇒ Ballonizer

Receive a untainted json (assume as validated by #valid_submit_json?) and add it to the database.

Parameters:

  • submit_json (String)

    A untainted JSON string. Validated with #valid_submit_json?.

  • time (Time) (defaults to: nil)

    A Time instance to be used in place of Time.now. Optional.

Returns:

Raises:

  • (SecurityError)

    If the input is tainted.



349
350
351
352
# File 'lib/ballonizer.rb', line 349

def process_submit_json(submit_json, time = nil)
  fail SecurityError, 'the input is tainted' if submit_json.tainted?
  process_submit_hash(JSON.parse(submit_json), time)
end

#valid_submit_hash?(submit_hash, throw_exceptions = false) ⇒ true, false

Note:

This is a instance method because, in the future, the validation can depend of instance settings.

Act as #valid_submit_json, but over a already parsed json and don’t (un)taint the hash.

Parameters:

  • submit_hash (Hash)

    A parsed JSON.

Returns:

  • (true, false)

Raises:

See Also:



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/ballonizer.rb', line 278

def valid_submit_hash?(submit_hash, throw_exceptions=false)
  if submit_hash.empty?
    fail SubmitError, "the submit request is empty"
  end

  submit_hash.each do | img_src, ballons |
    unless img_src.is_a?(String)
      # TODO: validate if valid URI?
      # TODO: define img_src max lenght?
      fail SubmitError, "the image src is a '#{img_src.class}' and not a String"
    end
    unless Addressable::URI.parse(img_src).absolute?
      fail SubmitError, "the image src ('#{img_src.class}') is not an absolute URI"
    end
    unless ballons.is_a?(Array)
      fail SubmitError, "the image with src '#{img_src}' is key of a " +
                        "'#{ballons.class}' and not a Array"
    end

    ballons.each do | ballon |
      unless ballon["text"].is_a?(String)
        fail SubmitError, "the ballon text is a '#{ballon.class}' and not" +
                          " a String"
      end
      if ballon["text"].empty?
        fail SubmitError, "the ballon text is empty"
      end
      [:top, :left, :width, :height, :font_size].each do | numeric_attr_name |
        numeric_attr = ballon[numeric_attr_name.to_s]
        unless numeric_attr.is_a?(Fixnum) || numeric_attr.is_a?(Float)
          fail SubmitError, "the #{numeric_attr_name} " +
                            "(#{numeric_attr}) isn't a Fixnum or " +
                            "Float (is a '#{numeric_attr.class}')"
        end
      end
      [:top, :left, :width, :height].each do | bound_name |
        bound = ballon[bound_name.to_s]
        unless bound >= 0 && bound <= 1
          fail SubmitError, "the #{bound_name.to_s} (#{bound.to_s}) isn't"
                            " between 0 and 1 (both inclusive)"
        end
      end

      ballon_end = {}
      ballon_end[:x] = ballon["left"] + ballon["width"]
      ballon_end[:y] = ballon["top"] + ballon["height"]

      [:x, :y].each do | axis |
        if ballon_end[axis] > 1
          side = { x: "right side", y: "bottom" }[axis]
          fail SubmitError, "the ballon with text #{ballon["text"].to_s} " +
                            "is trespassing the #{side} of the image"
        end
      end
    end
  end

  # if pass everything above return true
  true
rescue SubmitError => exception
  # HACK: "don't use exceptions for flow control", but this is the most DRY
  # way...
  if throw_exceptions then raise exception else false end
end

#valid_submit_json?(submit_json, throw_exceptions = false) ⇒ true, false

Note:

This is a instance method because, in the future, the validation can depend of instance settings.

Verify if the json is a valid output from the client counterpart. If the argument is valid untaint, otherwise taint (unless it’s frozen). If the second parameter argument is true the method will throw exceptions when the input is invalid.

Parameters:

  • submit_json (String)

    A JSON String.

  • throw_exceptions (FalseClass, TrueClass) (defaults to: false)

    Define behaviour when the input is invalid. If true throw exceptions, otherwise only return false. Default value: false (don’t throw exceptions).

Returns:

  • (true, false)

Raises:

See Also:



259
260
261
262
263
264
265
266
267
268
# File 'lib/ballonizer.rb', line 259

def valid_submit_json?(submit_json, throw_exceptions=false)
  parsed_submit = JSON.parse(submit_json)
  valid_submit_hash?(parsed_submit, true)
  submit_json.untaint unless submit_json.frozen?
  true
rescue JSON::ParserError, SubmitError => e
  submit_json.taint unless submit_json.frozen?
  raise e if throw_exceptions
  false
end