text:
function performSubmission() {
    var grader = new Grader();

    grader.addTest(function() {
            return typeof EventTracker === 'function';
        }, {
            wrongMessage: '"EventTracker" should exist and be a function.'
        },
        false
    );

    // test `on` method exists
    grader.addTest(function() {
            var tracker = new EventTracker();
            return typeof tracker.on === 'function';
        }, {
            wrongMessage: 'An `EventTracker` instance should have an `on` method.'
        }
    );

    // test `notify` method exists
    grader.addTest(function() {
            var tracker = new EventTracker();
            return typeof tracker.notify === 'function';
        }, {
            wrongMessage: 'An `EventTracker` instance should have an `notify` method.'
        }
    );

    // not used in quiz
    // grader.addTest(function() {
    //         var tracker = new EventTracker();
    //         return typeof tracker.watch === 'function';
    //     }, {
    //         wrongMessage: 'An `EventTracker` instance should have an `watch` method.'
    //     }
    // );

    // test `trigger` method exists
    grader.addTest(function() {
            var tracker = new EventTracker();
            return typeof tracker.trigger === 'function';
        }, {
            wrongMessage: 'An `EventTracker` instance should have an `trigger` method.'
        }
    );

    // test all method exists
    grader.addTest(function() {
            var tracker = new EventTracker();

            var hasOnMethod = typeof tracker.on === 'function'
            // var hasWatchMethod = typeof tracker.watch === 'function';
            var hasNotifyMethod = typeof tracker.notify === 'function';
            var hasTriggerMethod = typeof tracker.trigger === 'function';

            return hasOnMethod && hasNotifyMethod && hasTriggerMethod;
        }, {
            wrongMessage: 'The `EventTracker` instance does not have all the required methods.'
        },
        false
    );

    // test `trigger` method
    grader.addTest(function() {
            var info = 'ice cream';

            function celebrate( data ) {
                celebrate.callCount++;
                celebrate.message = data;
            }

            celebrate.callCount = 0;
            celebrate.message = '';

            var richardTracker = new EventTracker( 'Richard' );
            richardTracker.on( 'mainEvent', celebrate );
            richardTracker.trigger( 'mainEvent', info );

            return celebrate.callCount === 1 && celebrate.message === info;
        }, {
            wrongMessage: '`trigger`ing an instance did not run the correct code setup by `on`'
        }
    );

    // test `notify` method
    grader.addTest(function() {
            var info = 'ice cream';

            function celebrate( data ) {
                celebrate.callCount++;
                celebrate.message = data;
            }

            celebrate.callCount = 0;
            celebrate.message = '';

            var richardTracker = new EventTracker( 'Richard' );
            var tracker2 = new EventTracker( 'nephewParty' );

            tracker2.notify( richardTracker, 'mainEvent' );
            richardTracker.on( 'mainEvent', celebrate );
            tracker2.trigger( 'mainEvent', info );

            return celebrate.callCount === 1 && celebrate.message === info;
        }, {
            wrongMessage: 'EventTracker instance A did not correctly `notify` EventTracker instance B.'
        }
    );

    // // test `watch` method
    // grader.addTest(function() {
    //         var info = 'ice cream';
    //
    //         function celebrate( data ) {
    //             celebrate.callCount++;
    //             celebrate.message = data;
    //         }
    //
    //         celebrate.callCount = 0;
    //         celebrate.message = '';
    //
    //         var richardTracker = new EventTracker( 'Richard' );
    //         var tracker2 = new EventTracker( 'nephewParty' );
    //
    //         richardTracker.watch( tracker2, 'mainEvent' );
    //         richardTracker.on( 'mainEvent', celebrate );
    //         tracker2.trigger( 'mainEvent', info );
    //
    //         return celebrate.callCount === 1 && celebrate.message === info;
    //     }, {
    //         wrongMessage: 'EventTracker instance A did not correctly `watch` EventTracker instance B.'
    //     }
    // );

    grader.runTests();

    return{
        is_correct: grader.isCorrect,
        test_feedback: grader.getFormattedWrongMessages('\n\n'),
        test_comments: grader.getFormattedComments(),
        congrats: 'Great job!'
    }
}


text:
class Bill:

    def __init__(self, amount):
        self.amount = amount
        # Bill.money_holder[self] = 0

    def __str__(self):
        return "A {}$ bill".format(self.amount)

    def __int__(self):
        # return int(self.amount)
        return self.amount

    def __eq__(self, other):
        return self.amount == other.amount

    def __repr__(self):
        return self.__str__()

    def __hash__(self):
        return hash(self.amount)

    def total(self):
        return self.amount

    def get_bills(self):
        return str(self)

    def to_dictionary(self):
        return {self.amount: 1}

a = Bill(10)
b = Bill(5)
c = Bill(10)

print (a)
print (int(a) == 10)
print (str(a) == "A 10$ bill")

print (a == b)
print (a == c)

money_holder = {}

money_holder[a] = 1

if c in money_holder:
    money_holder[c] += 1

print (money_holder)

print (10 * '-')

class BatchBill:

    def __init__(self, bills):
        self.bills = bills

    def __getitem__(self, index):
        return self.bills[index]

    def __len__(self):
        return len(self.bills)

    def get_bills(self):
        return [str(x) for x in self.bills]

    def total(self):
        return sum([int(x) for x in self.bills])

    def to_dictionary(self):
        return {int(x): self.bills.count(x) for x in self.bills}

values = [10, 20, 50, 100]

# bills is a list of Bill objects
bills = [Bill(value) for value in values]

# batch is a Batchbill object that has a value - list of Bill objects
batch = BatchBill(bills)

for bill in batch:
    print (bill)

print (len(batch))
print (batch.total())

print (10 * '-')

class CashDesk:

    def __init__(self):
        self.cashdesk_money = 0
        self.bills_dict = {}

    def take_money(self, money):
        self.cashdesk_money += money.total()
        converted_money = money.to_dictionary()

        for key in converted_money.keys():
            if int(key) in self.bills_dict.keys():
                self.bills_dict[key] += converted_money[key]
            else:
                self.bills_dict[key] = converted_money[key]

    def total(self):
        return self.cashdesk_money

    def inspect(self):
        to_pairs = [(key, self.bills_dict[key])
                    for key in self.bills_dict.keys()]
        to_pairs.sort()

        for elem in to_pairs:
            print ("{}$  bills - {}".format(elem[0], elem[1]))

values = [10, 20, 50, 100, 100, 100]
bills = [Bill(value) for value in values]

batch = BatchBill(bills)

desk = CashDesk()

desk.take_money(batch)
desk.take_money(Bill(10))

print ("We have a total of {}$ in the desk.".format(desk.total()))

desk.inspect()

redpajama_set_name: RedPajamaGithub


text:
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }

var _fs = require('fs');

var _fs2 = _interopRequireDefault(_fs);

var _path = require('path');

var _path2 = _interopRequireDefault(_path);

var _cheerio = require('cheerio');

var _cheerio2 = _interopRequireDefault(_cheerio);

var _iceCap = require('ice-cap');

var _iceCap2 = _interopRequireDefault(_iceCap);

var _DocBuilderJs = require('./DocBuilder.js');

var _DocBuilderJs2 = _interopRequireDefault(_DocBuilderJs);

var _utilJs = require('./util.js');

/**
 * Index output builder class.
 */

var IndexDocBuilder = (function (_DocBuilder) {
  /**
   * create instance.
   * @param {Taffy} data - doc object database.
   * @param {ESDocConfig} config - use config to build output.
   * @param {CoverageObject} coverage - use coverage to build output.
   */

  function IndexDocBuilder(data, config, coverage) {
    _classCallCheck(this, IndexDocBuilder);

    _get(Object.getPrototypeOf(IndexDocBuilder.prototype), 'constructor', this).call(this, data, config);
    this._coverage = coverage;
  }

  _inherits(IndexDocBuilder, _DocBuilder);

  _createClass(IndexDocBuilder, [{
    key: 'exec',

    /**
     * execute building output.
     * @param {function(html: string, filePath: string)} callback - is called with output.
     */
    value: function exec(callback) {
      var ice = this._buildLayoutDoc();
      var title = this._getTitle();
      ice.load('content', this._buildIndexDoc());
      ice.text('title', title, _iceCap2['default'].MODE_WRITE);
      callback(ice.html, 'index.html');
    }
  }, {
    key: '_buildIndexDoc',

    /**
     * build index output.
     * @returns {string} html of index output.
     * @private
     */
    value: function _buildIndexDoc() {
      if (!this._config.index) return 'API Document';

      var indexContent = _fs2['default'].readFileSync(this._config.index, { encode: 'utf8' }).toString();

      var html = this._readTemplate('index.html');
      var ice = new _iceCap2['default'](html);
      var ext = _path2['default'].extname(this._config.index);
      if (['.md', '.markdown'].includes(ext)) {
        ice.load('index', (0, _utilJs.markdown)(indexContent));
      } else {
        ice.load('index', indexContent);
      }

      var result = ice.html;
      if (this._config.coverage) {
        var $ = _cheerio2['default'].load(result);
        $('.esdoc-coverage').html(this._buildCoverageHTML(this._coverage));
        result = $.root().html();
      }

      return result;
    }
  }]);

  return IndexDocBuilder;
})(_DocBuilderJs2['default']);

exports['default'] = IndexDocBuilder;
module.exports = exports['default'];


text:
package Boj_2920;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main {
	public static void main(String args[]) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		String line = br.readLine();
		String[] strs = line.split(" ");

		br.close();

		int before = Integer.parseInt(strs[0]);  // ¹®ÀÚ¿ °ªÀ» ¼ýÀÚ·Î º¯È¯
		 										 // beforeÀº strs[0](¹®ÀÚ¿ °ª)À» ¼ýÀÚ·Î º¯È¯½ÃŲ °Í.
		boolean isAscending = false;
		boolean isDescending = false;

		for (int i = 1; i < strs.length; i++) {
			int num = Integer.parseInt(strs[i]);

			if (before < num) {
				isAscending = true;
			} else if (before > num) {
				isDescending = true;
			} else {
				isAscending = false;
				isDescending = false;
			}
			before = num;
		}
		if (isAscending && isDescending) {
			System.out.println("mixed");
		} else if (isAscending && !isDescending) {
			System.out.println("ascending");
		} else {
			System.out.println("descending");
		}

	}

}


text:
ORCHESTRA Bundles
=====================

Orchestra is not just the description you just read above. It also allows you to create your own CMS.
It's a CMF easy to use, build your own template (layout), add your own custom block with your own logic,
build all the widget you need. That it what Orcherstra is made for.

## Structure of the framework

The bundle has been split into 2 directories :

**BootStrap**

``` bash
* AclManagerBundle : A bundle which provides classes to run ACL Manager`s utilities for Symfony2
* AdminBundle : A bundle which overload the SonataAdminBundle
* CacheBundle : A bundle which provides classes to cache handlers (memcache, files)
* DatabaseBundle : A bundle which provides classes and commands to run DB vendor`s utilities to backup and restore databases.
* MediaBundle : A bundle which overload the SonataMediaBundle.
* TranslationBundle : A bundle which provides models of classes allowing to work and develop with Gedmo translation and Gedmo tree, and a command to
generate orchestra bundle with a CRUD system of an entity
* TranslatorBundle : A bundle which provides entity and models of classes allowing to work with translation words.
* UserBundle : the current one, contains core libraries and services
```

**PiApp**

``` bash
* AdminBundle : A bundle which construct all the CMF with all managers of the creation of page with blocks and widgets.
* GedmoBundle : A bundle which is used to create a project with the CMF.
* TemplateBundle :  A bundle which is used to stock all template of layout and others.
```

## Dependencies

Register all bundle in your `app/AppKernel.php` file:

``` php

    public function registerBundles()
    {
              $bundles = array(
                new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
                new Symfony\Bundle\SecurityBundle\SecurityBundle(),
                new Symfony\Bundle\TwigBundle\TwigBundle(),
                new Symfony\Bundle\MonologBundle\MonologBundle(),
                new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
                new Symfony\Bundle\AsseticBundle\AsseticBundle(),
                new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),

                # secure
                new JMS\AopBundle\JMSAopBundle(),
                new JMS\DiExtraBundle\JMSDiExtraBundle($this),
                new JMS\SecurityExtraBundle\JMSSecurityExtraBundle(),
                new JMS\TranslationBundle\JMSTranslationBundle(),

                # doctrine
                new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
                new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(),
                new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),

                # route
                new BeSimple\I18nRoutingBundle\BeSimpleI18nRoutingBundle(),

                # sonata admin
                new Sonata\AdminBundle\SonataAdminBundle(),
                new Sonata\AdminBundle\SonataNotificationBundle(),
                new Sonata\EasyExtendsBundle\SonataEasyExtendsBundle(),
                new Sonata\DoctrineORMAdminBundle\SonataDoctrineORMAdminBundle(),
                new Sonata\CacheBundle\SonataCacheBundle(),
                new Sonata\BlockBundle\SonataBlockBundle(),
                new Sonata\jQueryBundle\SonatajQueryBundle(),
                new Sonata\MediaBundle\SonataMediaBundle(),

                # tools
                new FOS\UserBundle\FOSUserBundle(),
                new Knp\Bundle\MenuBundle\KnpMenuBundle(),
                new Knp\Bundle\PaginatorBundle\KnpPaginatorBundle(),
                new Knp\Bundle\SnappyBundle\KnpSnappyBundle(),

                # boostrap
                new BootStrap\DatabaseBundle\BootStrapDatabaseBundle(),
                new BootStrap\CacheBundle\BootStrapCacheBundle(),
                new BootStrap\AclManagerBundle\BootStrapAclManagerBundle(),
                new BootStrap\AdminBundle\BootStrapAdminBundle(),
                new BootStrap\UserBundle\BootStrapUserBundle(),
                new BootStrap\TranslationBundle\BootStrapTranslationBundle(),
                new BootStrap\TranslatorBundle\BootStrapTranslatorBundle(),
                new BootStrap\MediaBundle\BootStrapMediaBundle(),

                # trades
                new PiApp\AdminBundle\PiAppAdminBundle(),
                new PiApp\GedmoBundle\PiAppGedmoBundle(),
                new PiApp\TemplateBundle\PiAppTemplateBundle(),
        );

```

## composer.json

Register all namespace before using the bundle. Add the following line to your `app/autoload.php`

``` json

    {
        "name": "symfony/framework-standard-edition",
        "license": "MIT",
        "type": "project",
        "description": "The \"Symfony Standard Edition\" distribution",
        "repositories": [
        {
            "type":"vcs",
            "url":"https://github.com/vincecore/BeSimpleI18nRoutingBundle.git"
        }
        ],
        "require": {
            "php": ">=5.3.3",
            "symfony/symfony": "2.2.4",
            "doctrine/orm": "2.3",
            "doctrine/doctrine-bundle": "1.2.0",
            "twig/extensions": "1.0.0",
            "symfony/assetic-bundle": "2.1.3",
            "symfony/swiftmailer-bundle": "2.2.4",
            "symfony/monolog-bundle": "2.2.0",
            "sensio/distribution-bundle": "2.2.4",
            "sensio/framework-extra-bundle": "2.2.4",
            "sensio/generator-bundle": "2.3.2",

            "jms/translation-bundle": "1.1.*@dev",
            "jms/security-extra-bundle": "1.4.*",
            "jms/di-extra-bundle": "1.3.*",

            "gedmo/doctrine-extensions": "2.3.*@dev",
            "doctrine/doctrine-fixtures-bundle": "2.1.*@dev",
            "doctrine/data-fixtures": "1.0.*@dev",
            "stof/doctrine-extensions-bundle": "1.1.0",

            "friendsofsymfony/user-bundle": "2.0.*@dev",
            "besimple/i18n-routing-bundle": "2.2.x-dev",

            "imagine/Imagine": "*@stable",
            "kriswallsmith/buzz": "0.*",

            "knplabs/knp-menu-bundle": ">=1.1,<2.0",
            "knplabs/knp-menu": "1.1.*",
            "knplabs/knp-components": "1.2.2",
            "knplabs/knp-paginator-bundle": "2.3.*@dev",
            "knplabs/knp-snappy": "dev-master",
            "knplabs/knp-snappy-bundle": "dev-master",

            "sonata-project/intl-bundle": "2.1.*",
            "sonata-project/exporter": "1.*",
            "sonata-project/admin-bundle": "2.2.*@dev",
            "sonata-project/block-bundle": ">=2.2.1,<3.0",
            "sonata-project/cache-bundle": "2.1.*@dev",
            "sonata-project/doctrine-orm-admin-bundle": "2.2.*@dev",
            "sonata-project/easy-extends-bundle": "2.1.*@dev",
            "sonata-project/jquery-bundle": "1.8.*@dev",
            "sonata-project/media-bundle": "2.2.*@dev",
            "sonata-project/notification-bundle": "2.2.*@dev"
        },
        "scripts": {
            "post-install-cmd": [
                "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
                "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
                "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
                "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile"
            ],
            "post-update-cmd": [
                "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
                "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
                "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
                "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile"
            ]
        },
        "config": {
            "bin-dir": "bin"
        },
        "minimum-stability": "dev",
        "extra": {
            "symfony-app-dir": "app",
            "symfony-web-dir": "web",
            "branch-alias": {
                "dev-master": "2.2-dev"
            }
        },
        "autoload": {
            "psr-0": {
                "PiApp": "vendor/Orchestra",
                "BootStrap": "vendor/Orchestra",
                "Zend_": "vendor/Zend/library"
            }
        }
    }

```

## Installation

### Step 0: Configuring Serveur

Before starting coding, make sure that your local system is properly
configured for Symfony.

Execute the `check.php` script from the command line:

``` bash

    php app/check.php

```

**Required** :

- PHP with at least version 5.3.2 of PHP
- Sqlite3 must be enabled
- JSON must be enabled
- Ctype must be enabled
- PHP-XML module must be installed
- Installtion the gd library (for images): apt-get install php5-gd [command linux]
- PHP.ini must have the extensions:

    - date.timezone
    - php_fileinfo.dll
    - PDO_SQLITE.dll
    - php_intl.dll
    - php_memcache.dll (facultatif pour une gestion performante de cache de doctrine)
    - php_curl.dll

**Doctrine** :

To use Doctrine, you will need to have installed PDO. And you must have installed the PDO driver for the database server you want to use.

### Step 1: Configuring parameters  BDD and mailer

* Open the file orchestra_project / app / config / parameters.ini.
* Give the name "mydatabase" for example in the database and choose the type pdo_mysql to use a MySQL database.
* Give your user and password of your Gmail count.
* Change the secret code that will be used to protect your application from XSS attacks.

``` bash
[parameters]
    database_driver="pdo_mysql"
    database_host="localhost"
    database_port=""
    database_name="mydatabase"
    database_user="root"
    database_password=""
    mailer_transport="gmail"
    mailer_host=""
    mailer_user="MyUserGMAIL"
    mailer_password="MyPswGMAIL"
    locale="en_GB"
    secret="5b5a0ff57bd45284dafe7f104fc7d8e15"
```

### Step 2: Setting up Permissions

* The directories app / cache app / logs should be writable by both the web server and the user.
* On a UNIX system, if your web server is different from your user, you can run the following commands once in your project to ensure that the permissions are correctly installed.
* We must change www-data on your web server.

Many systems allow you to use ACL chmod a +.
**For more information** : http://symfony.com/doc/current/book/installation.html

Then you must add the uploads/media folder to allow specific users to load :

``` bash
mkdir app/cache/ Backup
mkdir app/cache/ Indexation
mkdir app/cache/ media
chmod –R 0777 app / cache
chmod –R 0777 app / log
chmod –R 0777 Orcehstra / BootStrap/TranslatorBundle/Resources/translations

mkdir web/uploads
mkdir web/uploads/media
mkdir web/yui
chmod –R 0777 web/uploads
chmod –R 0777 web/yui
```

### Step 4: Installing the vendor

As Symfony uses [Composer][2] to manage its dependencies, the recommended way
to create a new project is to use it.

If you don't have Composer yet, download it following the instructions on
http://getcomposer.org/ or just run the following command:

``` bash

    curl -s http://getcomposer.org/installer | php

```

**Setting vendor with composer.json**
``` bash

    php  composer.phar selfupdate
    php  composer.phar install -v

```

### Step 5: Create database, tables and fixtures

- There's no way to configure these defaults inside Doctrine, as it tries to be as agnostic as possible in terms of environment configuration.
- One way to solve this problem is to configure server-level defaults.

**Setting UTF8 defaults for MySQL is as simple as adding a few lines to your configuration file (typically my.cnf)**

``` bash

    [mysqld]
    collation-server     = utf8_general_ci
    character-set-server = utf8

```

**or open file \Doctrine\DBAL\Platforms\AbstractPlatform in getCreateTableSQL method and add this following line**

``` bash

    $options = $table->getOptions();
    ...
    $options['charset'] = 'utf8';
    $options['collate'] = 'utf8_general_ci';

```

- Open your console (cmd) or Putty.
- Go to the root of the application orchestra_project.

**Type the following command to create the database**

``` bash

    php  app/console  doctrine:database:create

```

**Type the following command to create the tables**

``` bash

    php  app/console  doctrine:schema:create

```

**Type the following command to install fixtures of the tables**

``` bash

    php  app/console  doctrine:fixtures:load

```

**For more information** : http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html

### Step 6: Connexion on /login

To connect as default super administrator:

``` bash

    Username: superadmin
    Password: superadmin

```

**The password must be changed at the first use.**


text:
class CTFOScreen : public CItem
{
public:
	DECLARE_CLASS(CTFOScreen, CItem);
	DECLARE_DATADESC();

	CTFOScreen()
	{
		color32 col32 = { 255, 25, 25, 220 };
		m_GlowColor = col32;
	}

	void Spawn(void);
	void Precache(void);
	void Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value);

private:

	string_t szFileName;
	COutputEvent	m_OnUse;	// Output when somebody clicks us

};

LINK_ENTITY_TO_CLASS(tfo_screenoverlay, CTFOScreen);

BEGIN_DATADESC(CTFOScreen)

// Links our input name from Hammer to our input member function
DEFINE_KEYFIELD(szFileName, FIELD_STRING, "ScriptFile"),
DEFINE_OUTPUT(m_OnUse, "OnUse"),

END_DATADESC()
PRECACHE_REGISTER(tfo_screenoverlay);

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFOScreen::Spawn(void)
{
	Precache();
	SetModel("models/props/note.mdl");
	AddEffects(EF_NOSHADOW | EF_NORECEIVESHADOW);

	BaseClass::Spawn();
}

void CTFOScreen::Precache(void)
{
	PrecacheModel("models/props/note.mdl");
}

void CTFOScreen::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) //If player used item
{
	if (!pActivator)
		return;

	if (!pActivator->IsPlayer())
		return;

	// Access our player class.
	CBasePlayer *pClient = ToBasePlayer(pActivator);
	if (!pClient)
		return;

	m_OnUse.FireOutput(this, this);
	EmitSound("ItemDraw.Paper");

	// Open journal / save station.
	engine->ClientCommand(pClient->edict(), "tfo_gameui_command OpenNotePanel\n");

	// This passes in our actual texture to display and not to mention it is catched in the inventory too, so it will be added for storage there... OR we could do a direct access from the note on the client ?
	g_pGameRules->ShowNote(szFileName.ToCStr());
}


text:
namespace df
{
class DrapeEngine;
}

namespace storage
{
class CountryInfoGetter;
}

namespace routing
{
class NumMwmIds;
}

class Index;

struct RoutePointInfo
{
  std::string m_name;
  RouteMarkType m_markType = RouteMarkType::Start;
  int8_t m_intermediateIndex = 0;
  bool m_isPassed = false;
  bool m_isMyPosition = false;
  m2::PointD m_position;
};

class RoutingManager final
{
public:
  class Delegate
  {
  public:
    virtual void OnRouteFollow(routing::RouterType type) = 0;
    virtual void RegisterCountryFilesOnRoute(std::shared_ptr<routing::NumMwmIds> ptr) const = 0;

    virtual ~Delegate() = default;
  };

  struct Callbacks
  {
    using IndexGetterFn = std::function<Index &()>;
    using CountryInfoGetterFn = std::function<storage::CountryInfoGetter &()>;

    Callbacks(IndexGetterFn && featureIndexGetter, CountryInfoGetterFn && countryInfoGetter)
      : m_indexGetter(std::move(featureIndexGetter))
      , m_countryInfoGetter(std::move(countryInfoGetter))
    {}

    IndexGetterFn m_indexGetter;
    CountryInfoGetterFn m_countryInfoGetter;
  };

  using RouteBuildingCallback =
      std::function<void(routing::IRouter::ResultCode, storage::TCountriesVec const &)>;
  using RouteProgressCallback = std::function<void(float)>;

  RoutingManager(Callbacks && callbacks, Delegate & delegate);

  void SetBookmarkManager(BookmarkManager * bmManager);

  routing::RoutingSession const & RoutingSession() const { return m_routingSession; }
  routing::RoutingSession & RoutingSession() { return m_routingSession; }
  void SetRouter(routing::RouterType type);
  routing::RouterType GetRouter() const { return m_currentRouterType; }
  bool IsRoutingActive() const { return m_routingSession.IsActive(); }
  bool IsRouteBuilt() const { return m_routingSession.IsBuilt(); }
  bool IsRouteBuilding() const { return m_routingSession.IsBuilding(); }
  bool IsRouteRebuildingOnly() const { return m_routingSession.IsRebuildingOnly(); }
  bool IsRouteNotReady() const { return m_routingSession.IsNotReady(); }
  bool IsRouteFinished() const { return m_routingSession.IsFinished(); }
  bool IsOnRoute() const { return m_routingSession.IsOnRoute(); }
  bool IsRoutingFollowing() const { return m_routingSession.IsFollowing(); }
  void BuildRoute(uint32_t timeoutSec);
  void SetUserCurrentPosition(m2::PointD const & position);
  void ResetRoutingSession() { m_routingSession.Reset(); }
  // FollowRoute has a bug where the router follows the route even if the method hads't been called.
  // This method was added because we do not want to break the behaviour that is familiar to our
  // users.
  bool DisableFollowMode();
  /// @TODO(AlexZ): Warning! These two routing callbacks are the only callbacks which are not called
  /// in the main thread context.
  /// UI code should take it into an account. This is a result of current implementation, that can
  /// be improved:
  /// Drape core calls some RunOnGuiThread with "this" pointers, and it causes crashes on Android,
  /// when Drape engine is destroyed
  /// while switching between activities. Current workaround cleans all callbacks when destroying
  /// Drape engine
  /// (@see MwmApplication.clearFunctorsOnUiThread on Android). Better soulution can be fair copying
  /// of all needed information into
  /// lambdas/functors before calling RunOnGuiThread.
  void SetRouteBuildingListener(RouteBuildingCallback const & buildingCallback)
  {
    m_routingCallback = buildingCallback;
  }
  /// See warning above.
  void SetRouteProgressListener(RouteProgressCallback const & progressCallback)
  {
    m_routingSession.SetProgressCallback(progressCallback);
  }
  void FollowRoute();
  void CloseRouting(bool removeRoutePoints);
  void GetRouteFollowingInfo(location::FollowingInfo & info) const
  {
    m_routingSession.GetRouteFollowingInfo(info);
  }
  m2::PointD GetRouteEndPoint() const { return m_routingSession.GetEndPoint(); }
  /// Returns the most situable router engine type.
  routing::RouterType GetBestRouter(m2::PointD const & startPoint,
                                    m2::PointD const & finalPoint) const;
  routing::RouterType GetLastUsedRouter() const;
  void SetLastUsedRouter(routing::RouterType type);
  // Sound notifications for turn instructions.
  void EnableTurnNotifications(bool enable) { m_routingSession.EnableTurnNotifications(enable); }
  bool AreTurnNotificationsEnabled() const
  {
    return m_routingSession.AreTurnNotificationsEnabled();
  }
  /// \brief Sets a locale for TTS.
  /// \param locale is a string with locale code. For example "en", "ru", "zh-Hant" and so on.
  /// \note See sound/tts/languages.txt for the full list of available locales.
  void SetTurnNotificationsLocale(std::string const & locale)
  {
    m_routingSession.SetTurnNotificationsLocale(locale);
  }
  /// @return current TTS locale. For example "en", "ru", "zh-Hant" and so on.
  /// In case of error returns an empty string.
  /// \note The method returns correct locale after SetTurnNotificationsLocale has been called.
  /// If not, it returns an empty string.
  std::string GetTurnNotificationsLocale() const
  {
    return m_routingSession.GetTurnNotificationsLocale();
  }
  /// \brief When an end user is going to a turn he gets sound turn instructions.
  /// If C++ part wants the client to pronounce an instruction GenerateTurnNotifications (in
  /// turnNotifications) returns
  /// an array of one of more strings. C++ part assumes that all these strings shall be pronounced
  /// by the client's TTS.
  /// For example if C++ part wants the client to pronounce "Make a right turn." this method returns
  /// an array with one string "Make a right turn.". The next call of the method returns nothing.
  /// GenerateTurnNotifications shall be called by the client when a new position is available.
  void GenerateTurnNotifications(std::vector<std::string> & turnNotifications);

  void AddRoutePoint(RouteMarkData && markData);
  std::vector<RouteMarkData> GetRoutePoints() const;
  size_t GetRoutePointsCount() const;
  void RemoveRoutePoint(RouteMarkType type, int8_t intermediateIndex = 0);
  void RemoveIntermediateRoutePoints();
  void MoveRoutePoint(RouteMarkType currentType, int8_t currentIntermediateIndex,
                      RouteMarkType targetType, int8_t targetIntermediateIndex);
  void HideRoutePoint(RouteMarkType type, int8_t intermediateIndex = 0);
  bool CouldAddIntermediatePoint() const;
  bool IsMyPosition(RouteMarkType type, int8_t intermediateIndex = 0);

  void SetRouterImpl(routing::RouterType type);
  void RemoveRoute(bool deactivateFollowing);

  void CheckLocationForRouting(location::GpsInfo const & info);
  void CallRouteBuilded(routing::IRouter::ResultCode code,
                        storage::TCountriesVec const & absentCountries);
  void OnBuildRouteReady(routing::Route const & route, routing::IRouter::ResultCode code);
  void OnRebuildRouteReady(routing::Route const & route, routing::IRouter::ResultCode code);
  void OnRoutePointPassed(RouteMarkType type, int8_t intermediateIndex);
  void OnLocationUpdate(location::GpsInfo & info);
  void SetAllowSendingPoints(bool isAllowed)
  {
    m_trackingReporter.SetAllowSendingPoints(isAllowed);
  }

  void SetTurnNotificationsUnits(measurement_utils::Units const units)
  {
    m_routingSession.SetTurnNotificationsUnits(units);
  }
  void SetDrapeEngine(ref_ptr<df::DrapeEngine> engine, bool is3dAllowed);
  /// \returns true if altitude information along |m_route| is available and
  /// false otherwise.
  bool HasRouteAltitude() const;

  /// \brief Generates 4 bytes per point image (RGBA) and put the data to |imageRGBAData|.
  /// \param width is width of chart shall be generated in pixels.
  /// \param height is height of chart shall be generated in pixels.
  /// \param imageRGBAData is bits of result image in RGBA.
  /// \param minRouteAltitude is min altitude along the route in altitudeUnits.
  /// \param maxRouteAltitude is max altitude along the route in altitudeUnits.
  /// \param altitudeUnits is units (meters or feet) which is used to pass min and max altitudes.
  /// \returns If there is valid route info and the chart was generated returns true
  /// and false otherwise. If the method returns true it is guaranteed that the size of
  /// |imageRGBAData| is not zero.
  /// \note If HasRouteAltitude() method returns true, GenerateRouteAltitudeChart(...)
  /// could return false if route was deleted or rebuilt between the calls.
  bool GenerateRouteAltitudeChart(uint32_t width, uint32_t height, std::vector<uint8_t> & imageRGBAData,
                                  int32_t & minRouteAltitude, int32_t & maxRouteAltitude,
                                  measurement_utils::Units & altitudeUnits) const;

  uint32_t OpenRoutePointsTransaction();
  void ApplyRoutePointsTransaction(uint32_t transactionId);
  void CancelRoutePointsTransaction(uint32_t transactionId);
  static uint32_t InvalidRoutePointsTransactionId();

  /// \returns true if there are route points saved in file and false otherwise.
  bool HasSavedRoutePoints() const;
  /// \brief It loads road points from file and delete file after loading.
  /// \returns true if route points loaded and false otherwise.
  bool LoadRoutePoints();
  /// \brief It saves route points to file.
  void SaveRoutePoints() const;

private:
  void InsertRoute(routing::Route const & route);
  bool IsTrackingReporterEnabled() const;
  void MatchLocationToRoute(location::GpsInfo & info,
                            location::RouteMatchingInfo & routeMatchingInfo) const;
  location::RouteMatchingInfo GetRouteMatchingInfo(location::GpsInfo & info);
  uint32_t GenerateRoutePointsTransactionId() const;

  void SetPointsFollowingMode(bool enabled);

  void ReorderIntermediatePoints();
  std::vector<int8_t> PredictIntermediatePointsOrder(std::vector<m2::PointD> const & points);

  RouteBuildingCallback m_routingCallback = nullptr;
  Callbacks m_callbacks;
  ref_ptr<df::DrapeEngine> m_drapeEngine = nullptr;
  routing::RouterType m_currentRouterType = routing::RouterType::Count;
  routing::RoutingSession m_routingSession;
  Delegate & m_delegate;
  tracking::Reporter m_trackingReporter;
  BookmarkManager * m_bmManager = nullptr;

  std::vector<dp::DrapeID> m_drapeSubroutes;
  std::mutex m_drapeSubroutesMutex;

  std::unique_ptr<location::GpsInfo> m_gpsInfoCache;

  struct RoutePointsTransaction
  {
    std::vector<RouteMarkData> m_routeMarks;
  };
  std::map<uint32_t, RoutePointsTransaction> m_routePointsTransactions;

  DECLARE_THREAD_CHECKER(m_threadChecker);
};


text:
Introduction
============

Pod4 is not an ORM. No, really it isn't. Because that would be ridiculous.

...okay, it kind of acts a _bit_ like an ORM...

Ah, well. Judge for yourself. Here's a model:

    class CustomerModel < Pod4::Model

      class CustomerInterface < Pod4::PgInterface
        set_table  :customer
        set_id_fld :id
      end

      set_interface CustomerInterface.new(CONN_STRING)

      attr_columns :cust_code, :name, :group
    end

And here's a method that uses the model:

    def change_customer_group(id, group)
      customer = Customer.new(id).read.or_die
      customer.group = group
      customer.update.or_die
    end

Seriously now
-------------

Pod4 is a very simple set of classes that sits on top of some other library which gives access to
data -- for example, pg, tds, or Sequel (which _is_ an ORM...) It's relatively easy to get it to
talk to a new sort of data access library, and you're not limited to databases.

It provides a simple, common framework to talk to all these data sources, using model classes which
(to my mind at least) are clean, easy to understand and maintain, using a bare minimum of DSL and
vanilla Ruby inheritance.

This is the central motivation behind the project -- to provide a mechanism that allows for model
classes which actually represent your data to the rest of your code in a way that you are fully in
control of. Because it's your model classes, not the database, which are the canonical
representation of the data.

I don't want the people who maintain my code to have to know the differences between ActiveRecord's
`update` and `update_all`, or Sequel's `dataset[]` and `dataset.where()`.  Pod4::Model has a dozen
or so methods you need to worry about, and six of those are pretty much self-explanatory. Or, you
can inherit from Pod4::BasicModel instead, and do without even that.

I honestly don't think of it as an Object Relational Manager.  I think of it as a Way To Have Nice
Models.

If you are looking for something with all the features of, say, ActiveRecord, then this isn't for
you. I provide basic access to and maintenance of records, with validation. For anything more, you
need to be willing to use a very well established existing DSL within your model code -- SQL.

Thanks
======

This code was developed, by me, during working hours at [James Hall & Co.
Ltd](https://www.jameshall.co.uk/). I'm incredibly greatful that they have permitted me to
open-source it.

Installation
============

    gem install pod4

Of course you will also need to install whatever other gems you need in order to access the data
you want Pod4 to see.  Currently there are interfaces for:

* Sequel (which itself of course talks to all manner of databases)
* Tiny_tds
* Pg
* Nebulous (my own STOMP wrapper/protocol/what-have-you)

(But, you can add your own interfaces. It's not hard.)

A Short Tutorial
================

(Don't Worry About) Octothorpe
------------------------------

Pod4 uses my Octothorpe gem to pass information around. An Octothorpe is basically a Hash, except
the keys are always symbols, and it's read only.

But you don't really need to know that here. If you mentally substitute "Hash" every time I say
"Octothorpe", you'll be fine.

Model and Interface
-------------------

Note well that we distinguish between 'models' and 'interfaces':

The model represents the data to your application, in the format that makes most sense to your
application: that might be the same format that it is stored in on the database, or it might not.
The model doesn't care about where the data comes from. Models are all subclasses of Pod4::Model
(or Pod4::BasicModel, but we'll leave that alone for now).

An interface encapsulates the connection to whatever is providing the data. It might be a wrapper
for calls to the Sequel ORM, for example. Or it could be a making a series of calls to a set of
Nebulous verbs. It only cares about dealing with the data source, and it is only called by the
model.

An interface is a seperate class, which is defined for each model. There are parent classes for a
number of the sources you will need, but failing that, you can always create one from the ultimate
parent, Pod4::Interface.

Simple Model Usage
------------------

    # find record 14; raise error otherwise. Update and save.
    x = ExampleModel.new(14).read.or_die
    x.two = "new value"
    x.update

    # create a new record from the params hash
    y = ExampleModel.new
    y.set(params)
    y.create

A model is a class, each instance of which represents a single record. on that instance you can
call the following for basic operation:

* `create` -- tells the data source to store this new "record"
* `read`   -- obtains the "record" from the data source
* `update` -- updates the "record" on the data source
* `delete` -- deletes the "record" on the data source.
* `set`    -- set the column attributes of the object with a hash or Octothorpe
* `to_ot`  -- output an Octothorpe of the object's column attributes
* `alerts` -- return an array of Alerts (which I'll explain later)

(Note that we say "record" not record. The data source might not be a database.  Your model
instance might be represented on the data source as several records, or something else entirely.)

There is one more operation - `list`. Call this on the model class itself, and it will return an
array of model objects that match the criteria you pass. What you can pass to list depends on
your model class (of course); by default it also depends on the interface the model uses. But
normally it should except a hash, like so:

    ExampleModel.list(:one => "a")  #-> Array of ExampleModel where one = "a"

Additionally, you can chain `or_die` onto any model method to get it to raise exceptions if
something is wrong on the model. If you don't want exceptions, you can check the model's
model_status attribute, or just look at the alerts.

Those eight (nine) methods are _all_ the methods given by Pod4::Model that you are normally going
to want to use, outside of the code actually inside your model.

A Simple Model
--------------

Here is the model and interface definition that goes with the above example:

    require 'pod4'
    require 'pod4/pg_interface'
    require 'pg'

    class ExampleModel < Pod4::Model

      class ExampleInterface < Pod4::PgInterface
        set_table :example
        set_id_fld :id, autoincrement: true
      end

      set_interface ExampleInterface.new($pg_conn)
      attr_columns :one, :two, :three
    end

In this example we have a model that relies on the Pg gem to talk to a table 'example'. The table
has a primary key field 'id' and columns which correspond to our three attributes one, two and
three.  There is no validation or error control.

Note that we have to require pg_interface and pg seperately. I won't bother to show this in any
more model examples.

### Interface ###

Let's start with the interface definition.  Remember, the interface class is only there to
represent the data source to the model. Yours will most likely be no more complex than the one
above. Since they are only accessed by the model, my preference is to define them in an internal
class, but if that makes you back away slowly waving your hands placatingly, put it in another
file. Pod4 is fine with that.

Inside your interface class you must call some DSLish methods to tell the interface how to talk to
the data. What they are depends on the interface, but the ones for PgInterface are pretty common:

* `set_schema` -- optional -- the name of the schema to find the table in
* `set_table`  -- mandatory -- the name of the database table to use
* `set_id_fld` -- mandatory -- the name of the column that makes the record unique

Actually, _every_ interface defines `set_id_fld`. Instances of a model _must_ be represented by a
single ID field that provides a unique identifier. Pod4 does not care what it's called or what data
type it is -- if you say that's what makes it unique, that's good enough. Additionally you can
specify whether your key autoincrements or not. If you don't say, we assume that it does.

Internally, Interfaces talk the same basic language of list / create / read / update / delete that
models do. Sometimes you might want to add a special interface method for a specific database
operation, but otherwise that's probably going to be it for your Interface definition.

### Model ###

Models have two of their own DSLish methods:

* `set_interface` -- here is where you instantiate your Interface class
* `attr_columns`  -- like `attr_accessor`, but letting the model know to care.

You can see that interfaces are instantiated when the model is required.  Exactly what you need to
pass to the interface to instantiate it depends on the interface. SequelInterface wants the Sequel
DB object; the other interfaces only want connection hashes.

(If you are finding it annoying to have to, for example, figure out your database connection hash
or get a Sequel DB object _before_ requiring your models, then see "Connections", below.)

Any attributes you define using `attr_columns` are treated specially by Pod4::Model. You get all
the effect of the standard Ruby `attr_accessor` call, but in addition, the attribute will be passed
to and from the interface, and to and from your external code, by the standard model methods.

In addition to the methods above, we have:

* `validate`         -- override this to provide validation
* `map_to_model`     -- controls how the interface sets attributes on the model
* `map_to_interface` -- controls how the model sends data to the interface
* `add_alert`        -- adds an alert to the model

A model also has some built-in attributes of its own:

* `model_id`     -- this is the value of the ID column you set in the interface.
* `model_status` -- one of :error :warning :okay :deleted :unknown

We'll deal with all these below.

Adding Validation
-----------------

Built into the model is an array of alerts (Pod4::Alert) which are messages that have been raised
against the instance of the model class. Each alert can have a type of :error, :warning, :info or
:success. If any alert has a type of :error or :warning, then that is reflected in the model's
`model_status` attribute. A model that has passed validation with no :error or :warning alerts is
status :okay.

(There are two other possible statuses -- models are :unknown when validation has yet to be run,
and :deleted after a call to delete.)

You can raise alerts yourself, and you normally do so by overriding `validate`.  This method is
called after a read as well as when you write to the database; so that a model object should always
have a model_status reflecting its "correctness" regardless of whether it came from the data source
or your application.

Here's a model with some validation:

    class Customer < Pod4::Model

      class CustomerInterface < Pod4::PgInterface
        set_schema :pod4example
        set_table  :customer
        set_id_fld :id, autoincrement: true
      end

      set_interface CustomerInterface.new($pg_conn)
      attr_columns :cust_code, :name, :group

      def validate(mode)
        super
        return if mode == :delete

        add_alert(:error, :name, "Name cannot be empty") \
          unless @name && @name =~ \^\s*$\

        add_alert(:error, :cust_code, "invalid customer code") \
          unless @cust_code && @cust_code.length == 6

      end

    end

(Note: as a general principal, you should always call super when overriding a method in Pod4 model,
unless you have good reason not to.)

Validation is run on create, read, update and delete.  If the model has a status of :error, then
the validation will fail.  (Probably you do not want this on delete; test the parameter passed
to validate as in the example above).

In passing I should note that validation is _not_ run on list: every record that list returns
should be complete, but the `model_status` will be :unknown because validation has not been run.
(This is partly for the sake of speed.)

You should be aware that validation is not called on `set`, either. Because of that, it's entirely
possible to set a model to an invalid state and not raise any alerts against it until you go to
commit to the database.  If you want to change the state of the model and then validate it before
that, you must call `validate` yourself.

Changine How a Model Represents Data
------------------------------------

Pod4 will do the basic work for you when it comes to data types. integers, decimals, dates and
datatimes should all end up as the right type in the model.  (It depends on the Interface. You're
going to get tired of me saying that, aren't you?) But maybe you want more than that.

Let's imagine you have a database table in PostreSQL with a column called cost that uses the money
type. (This is a terrible idea, by the way.) And you want it to be a `BigDecimal` in the model.
Well, Pod4 won't do that for you -- for all I know someone might have a problem with my requiring
BigDecimal -- but it's not hard to do yourself.

    class Product < Pod4::Model

      class ProductInterface < Pod4::PgInterface
        set_schema :pod4example
        set_table  :product
        set_id_fld :product_id, autoincrement: true
      end

      set_interface ProductInterface.new($pg_conn)
      attr_columns :description, :cost

      def map_to_model(ot)
        super
        @cost = Bigdecimal(@cost)
      end

      def map_to_interface
        super.merge(cost: @cost.to_f)
      end

    end

`map_to_model` gets called when the model wants to write data from the interface on the model; it
takes an Octothorpe from the interface as a parameter. By default it behaves as `set` does.

`map_to_interface` is the opposite: it gets called when the model wants to write data on the
interface from the model. It _returns_ an Octothorpe to the interface. By default it behaves as
`to_ot` does. (Since OTs are read only, you must modify it using merge.)

You might also want to ensure that your data types are honoured when your application updates a
model object; in which case you will need to override `set` as well.

If this seems like a lot of work, take a look at the Pod4::TypeCasting mixin. it will handle it for
you.

Relations
---------

Pod4 does not provide relations. But, I'm not sure that it needs to. Look:

    class BlogPost < Pod4::Model

      class BlogPostInterface < Pod4::PgInterface
        set_table  :blogpost
        set_id_fld :id, autoincrement: true
      end

      set_interface BlogPostInterface.new($conn)
      attr_columns :text

      def comments; Comment.list(post: @id); end
    end

    class Comment < Pod4::Model

      class CommentInterface < Pod4::PgInterface
        set_table  :comment
        set_id_fld :id
      end

      set_interface CommentInterface.new($conn)
      attr_columns :post_id, :text

      def blog_post; BlogPost.new(@post_id).read.or_die; end
    end

So the BlogPost model has a comments method that returns an array of Comments, and the Comments
model has a blog_post method that returns the BlogPost. (You would probably want to add validation
to enforce relational integrity.)

Is this approach inefficient?  Possibly. But if you don't like it, you can always try:

Beyond CRUD (& List)
--------------------

Sooner or later you will want to do something more than Pod4::Model will give you automatically.
There is a perfectly well documented, very popular DSL with lots of examples to solve this problem.
It's called SQL.

If your interface is connected to a SQL database, it should provide two more methods: `execute` and
`select`.

    class BlogPost < Pod4::Model

      class BlogPostInterface < Pod4::PgInterface
        set_table  :blogpost
        set_id_fld :id, autoincrement: true
      end

      set_interface BlogPostInterface.new($conn)
      attr_columns :text

      ##
      # return an array of hashes where each comment has the post joined to it
      #
      def post_and_comments
        interface.select( %Q|select *
                               from blogpost b
                               join comments c on(c.post_id = b.id);| )

      end

      ##
      # delete all comments on this post
      #
      def delete_comments
        interface.execute(
            %Q|delete from comments where post_id = #{@model_id};| )

      end

    end

Neither `execute` nor `select` care about the table or ID field you passed to the interface. They
only run pure SQL. The only difference between them is that select expects to return an array of
results.

To my way of thinking, there is absolutely nothing wrong about using SQL in a model. It will
certainly need revisiting if you change database. But how often does that happen, really?  And if
it ever does, you are likely to need to revisit the effected models anyway...

Connections
-----------

### A Couple Of Wrinkles ###

There are a couple of weird wrinkles in all this which you may or may not have missed.  Wrinkle
one:

    class MyModel < Pod4::Model

      class Interface < Pod4::PgInterface
        set_id_fld :id, autoincrement: true
        set_table  :my_table
      end

      set_interface Interface.new($pgconn)  # <- wrinkle
      attr_columns :one, two, three
    end

You _instantiate_ the interface at the point where you _define_ the model.  I admit this is a
little odd. It wasn't a specific design decision on my part; rather it was the cleanest way to get
where I was going.

In practice this means you need to get your DB connection details from somewhere, maybe create your
Sequel DB object; and only then can you require your models.

Leading on from this, wrinkle two: except when using Sequel (which behaves differently) each
interface has its own connection to the database. This means that your application has (simplifying
a bit here) one database connection for each model class.  So if you have a Customer model and a
Orders model, your application will hold two connections to the database.  All customer enquiries
share a single connection, and all order enquiries share a single connection.

I'm finding that, generally, this scales about right. But if you have a lot of different models and a
database that runs out of connections easily, it might be problematic.

### The Connection Object ###

The solution to both of these wrinkles, if you need one, is to use a Pod4::Connection object:

    #
    # init.rb -- bootup for my project
    #

    # Require libraries
    require "sequel"
    require "pod4"
    require "pod4/sequel_interface"
    require "pod4/connection"

    # require models
    $conn = Pod4::Connection.new(interface: Pod4::SequelInterface)
    require_relative "models/customer"

    # set up database connection
    hash = get_sequel_params
    $conn.data_layer_options = Sequel.connect(hash)

    #############

    #
    # models/customer.rb ( only a part shown)
    #

    class Foo < Pod4::Model
      class Interface < Pod4::SequelInterface
        set_table    :foo
        set_id_field :id, autoincrement: true
      end

      set_interface Interface.new($conn)

TL;DR: the only code that needs to go in the middle of your requires is the line defining a
Connection object; you pass that to the interface instead.  You can set up the parameters
the interface needs and pass them to the connection object afterwards.

With TdsInterface and PgInterface you can pass the same connection to multiple models and they will
share it.  These interfaces take a Pod4::ConnectionPool instead, but otherwise the code looks
exactly the same as the above example.

(Technical note: the ConnectionPool object will actually provide multiple connections, one per
thread that uses it.  This satisfies the requirement of the underlying libraries that connections
are not shared between threads, and therefore ensures that Pod4 is more or less thread safe.  You
get this functionality automatically -- if you don't define a ConnectionPool, then the interface
will create one internally. Sequel uses its own thread pool, of course, and we only use the one
Sequel DB object for the whole of Pod4, so it doesn't need any of that. That's why it uses
Pod4::Connection, instead.)

BasicModel
----------

Sometimes your model needs to represent data in a way which is so radically different from the data
source that the whole list, create, read, update, delete thing that Pod4::Model gives you is no
use.

Pod4::BasicModel gives you:

* `set_interface`
* the `model_id`, `model_status` and `alerts` attributes
* `add_alert`

...and nothing else. But that's enough to make a model, your way, using the methods on the
interface. These are the same CRUDL methods that Pod4::Model provides -- except that on the
interface, the CRUD methods take a record id as a key.

A real world example: at James Hall my intranet system has a User model, where each attribute is a
parameter that controls how the system behaves for that user -- email address, security settings,
etc.  Having one object to represent the user is the most sensible thing.

But I don't want to have to add a column to the database each time I change the intranet system and
add a user parameter. The logical place to change the parameter is in the User model, not in the
database, and certainly not both. So on the database, I have a settings table where the key runs:
userid, setting name.

Here's a simplified version of my User model. This one is read only, but it's hopefully enough to
get the idea:

    class User < Pod4::BasicModel

      class UserInterface < ::Pod4::SequelInterface
        set_table :settings
        set_id_fld :id, autoincrement: false
      end

      # Here we set what settings always exist for a user
      Setting = Struct.new(:setName, :default)

      DefaultSettings = [ Setting.new( :depot, nil ),
                          Setting.new( :store, nil ),
                          Setting.new( :menu,  nil ),
                          Setting.new( :roles, ''  ),
                          Setting.new( :name,  ''  ),
                          Setting.new( :nick,  ''  ),
                          Setting.new( :mail,  nil ) ]

      set_interface UserInterface.new($db)
      attr_reader :userid, :depot, :store, :menu, :roles, :name, :nick, :mail

      class << self
        def keys; DefaultSettings.map{|x| x.setName }; end

        def list
          array = interface.select(%Q|select distinct userid from settings;|)
          array.map {|r| self.new( r[:userid] ).read }
        end
      end # of class << self

      def initialize(userid=nil)
        super(userid)

        self.class.keys.each do |key|
          instance_variable_set( "@#{key}".to_sym, nil )
        end
      end

      def read
        lst = interface.list(userid: @model_id)

        data = lst.each_with_object({}) do |ot,h|
          h[ot.>>.setname] = ot.>>.setvalue
        end

        @userid = @model_id
        set_merge( Octothorpe.new(data) )
        validate; @model_status = :okay unless @model_status != :unknown

        self
      end

      def to_ot
        hash = self.class.keys.each_with_object({}) do |k,m|
          m[k] = instance_variable_get("@#{k}".to_sym)
        end

        Octothorpe.new(hash)
      end

      def set_merge(hash)
        self.class.keys.each do |key|
          value = hash[key]
          instance_variable_set( "@#{key}".to_sym, value ) if value
        end
      end

    end

Extensions
----------

There are some mixins that you can use to extend the functionality of Pod4 models.  Have a look
at the comments at the top of the mixin in question if you want details.

* typecasting -- force columns to be a specific ruby type, validation helpers, some encoding stuff
* encrypting  -- encrypt text columns
* tweaking    -- adds DSL commands to support custom methods on the interface.

Gotchas
-------

Some hopefully-not-too-unexpected behaviour:

* If you change attributes on a record, then call #delete or #read on it, we don't warn you that
  your changes are lost.

* If you attempt to update an autoincrement ID field, we don't write that change to the database
  and we don't warn you about that.

* As mentioned above, we only run validate on #create, #read, #update and #delete. So you can
  change the attributes of a record to something invalid without it immediately warning you. (You
  can always run #validate yourself, though.)

* Again, as mentioned above, the array of model instances returned by #list will all be status
  :unknown.  This is because we have run neither #read nor #validate against them.

* I can't stop you writing to `@model_id` or `@model_status` in your model.  I have no idea what
  might happen if you do, but I doubt that it would ever be a good idea.  (In a non-autoincrement
  model, write to your ID field directly instead; `@model_id` will be updated when you call
  #create, #update or of course #read.)


text:
layout: post
typo_id: 73
title: "Show your passion: get a t-shirt!"
---

I was planning to get a Ruby t-shirt. So I made the purchase today and bought two:

Use Ruby be Happy

Ruby on Rails

Ironically, what motivated me was this post about Java and people wearing Java t-shirts! No! The Rubyist in me cannot bear to see those guys around totting their Java stuff.


text:
using ::base::test::RunClosure;
using ::testing::_;

namespace {

constexpr char kDefaultTestUrl[] = "https://www.google.com/";

const char* const kTestPhysicalDeviceIds[] = {"1", "2", "3"};

constexpr uint16_t kVendorYubico = 0x1050;
constexpr uint16_t kProductYubicoGnubby = 0x0200;
constexpr uint16_t kUsageFidoU2f = 0x0001;

class HidChooserControllerTest : public ChromeRenderViewHostTestHarness {
 public:
  HidChooserControllerTest() = default;
  HidChooserControllerTest(HidChooserControllerTest&) = delete;
  HidChooserControllerTest& operator=(HidChooserControllerTest&) = delete;
  ~HidChooserControllerTest() override = default;

  permissions::MockChooserControllerView& view() {
    return mock_chooser_controller_view_;
  }
  MockHidDeviceObserver& device_observer() { return mock_device_observer_; }

  void SetUp() override {
    ChromeRenderViewHostTestHarness::SetUp();

    content::WebContentsTester* web_contents_tester =
        content::WebContentsTester::For(web_contents());
    web_contents_tester->NavigateAndCommit(GURL(kDefaultTestUrl));

    // Set fake HID manager for HidChooserContext.
    mojo::PendingRemote<device::mojom::HidManager> hid_manager;
    hid_manager_.Bind(hid_manager.InitWithNewPipeAndPassReceiver());
    base::RunLoop run_loop;
    auto* chooser_context = HidChooserContextFactory::GetForProfile(profile());
    chooser_context->SetHidManagerForTesting(
        std::move(hid_manager),
        base::BindLambdaForTesting(
            [&run_loop](std::vector<device::mojom::HidDeviceInfoPtr> devices) {
              run_loop.Quit();
            }));
    run_loop.Run();

    chooser_context->AddDeviceObserver(&mock_device_observer_);
  }

  void TearDown() override {
    auto* chooser_context = HidChooserContextFactory::GetForProfile(profile());
    chooser_context->RemoveDeviceObserver(&mock_device_observer_);

    ChromeRenderViewHostTestHarness::TearDown();
  }

  std::unique_ptr<HidChooserController> CreateHidChooserController(
      std::vector<blink::mojom::HidDeviceFilterPtr> filters,
      content::HidChooser::Callback callback = base::DoNothing()) {
    auto hid_chooser_controller = std::make_unique<HidChooserController>(
        main_rfh(), std::move(filters), std::move(callback));
    hid_chooser_controller->set_view(&mock_chooser_controller_view_);
    return hid_chooser_controller;
  }

  device::mojom::HidDeviceInfoPtr CreateAndAddFakeHidDevice(
      const std::string& physical_device_id,
      uint32_t vendor_id,
      uint16_t product_id,
      const std::string& product_string,
      const std::string& serial_number,
      uint16_t usage_page = device::mojom::kPageGenericDesktop,
      uint16_t usage = device::mojom::kGenericDesktopGamePad) {
    return hid_manager_.CreateAndAddDeviceWithTopLevelUsage(
        physical_device_id, vendor_id, product_id, product_string,
        serial_number, device::mojom::HidBusType::kHIDBusTypeUSB, usage_page,
        usage);
  }

  void ConnectDevice(const device::mojom::HidDeviceInfo& device) {
    hid_manager_.AddDevice(device.Clone());
  }

  void DisconnectDevice(const device::mojom::HidDeviceInfo& device) {
    hid_manager_.RemoveDevice(device.guid);
  }

  void UpdateDevice(const device::mojom::HidDeviceInfo& device) {
    hid_manager_.ChangeDevice(device.Clone());
  }

  blink::mojom::DeviceIdFilterPtr CreateVendorFilter(uint16_t vendor_id) {
    return blink::mojom::DeviceIdFilter::NewVendor(vendor_id);
  }

  blink::mojom::DeviceIdFilterPtr CreateVendorAndProductFilter(
      uint16_t vendor_id,
      uint16_t product_id) {
    return blink::mojom::DeviceIdFilter::NewVendorAndProduct(
        blink::mojom::VendorAndProduct::New(vendor_id, product_id));
  }

  blink::mojom::UsageFilterPtr CreatePageFilter(uint16_t usage_page) {
    return blink::mojom::UsageFilter::NewPage(usage_page);
  }

  blink::mojom::UsageFilterPtr CreateUsageAndPageFilter(uint16_t usage_page,
                                                        uint16_t usage) {
    return blink::mojom::UsageFilter::NewUsageAndPage(
        device::mojom::HidUsageAndPage::New(usage, usage_page));
  }

 private:
  device::FakeHidManager hid_manager_;
  permissions::MockChooserControllerView mock_chooser_controller_view_;
  MockHidDeviceObserver mock_device_observer_;
};

}  // namespace

TEST_F(HidChooserControllerTest, EmptyChooser) {
  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  // Create the HidChooserController. There should be no options.
  auto hid_chooser_controller = CreateHidChooserController({});
  options_initialized_loop.Run();

  EXPECT_EQ(0u, hid_chooser_controller->NumOptions());
}

TEST_F(HidChooserControllerTest, AddBlockedFidoDevice) {
  base::RunLoop device_added_loop;
  EXPECT_CALL(device_observer(), OnDeviceAdded(_))
      .WillOnce(RunClosure(device_added_loop.QuitClosure()));

  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  // 1. Connect a device blocked by vendor/product ID.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], kVendorYubico,
                            kProductYubicoGnubby, "gnubby", "001");
  device_added_loop.Run();

  // 2. Create the HidChooserController. The blocked device should be excluded.
  auto hid_chooser_controller = CreateHidChooserController({});
  options_initialized_loop.Run();

  EXPECT_EQ(0u, hid_chooser_controller->NumOptions());
}

TEST_F(HidChooserControllerTest, AddUnknownFidoDevice) {
  // Devices that expose a top-level collection with the FIDO usage page should
  // be blocked even if they aren't on the HID blocklist.
  base::RunLoop device_added_loop1;
  base::RunLoop device_added_loop2;
  EXPECT_CALL(device_observer(), OnDeviceAdded(_))
      .WillOnce(RunClosure(device_added_loop1.QuitClosure()))
      .WillOnce(RunClosure(device_added_loop2.QuitClosure()));

  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  // 1. Connect a device blocked by HID usage.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], 1, 1, "fido", "001",
                            device::mojom::kPageFido, kUsageFidoU2f);
  device_added_loop1.Run();

  // 2. Connect a second device blocked by HID usage.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[1], 2, 2, "fido", "002",
                            device::mojom::kPageFido, 0);
  device_added_loop2.Run();

  // 3. Create the HidChooserController. The blocked devices should be excluded.
  auto hid_chooser_controller = CreateHidChooserController({});
  options_initialized_loop.Run();

  EXPECT_EQ(0u, hid_chooser_controller->NumOptions());
}

TEST_F(HidChooserControllerTest, BlockedFidoDeviceAllowedWithFlag) {
  base::RunLoop device_added_loop;
  EXPECT_CALL(device_observer(), OnDeviceAdded(_))
      .WillOnce(RunClosure(device_added_loop.QuitClosure()));

  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  // 1. Allow WebHID to access devices on the HID blocklist.
  base::CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kDisableHidBlocklist);

  // 2. Connect a device with the FIDO usage page. The device uses
  // vendor/product IDs that are blocked by the HID blocklist.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], kVendorYubico,
                            kProductYubicoGnubby, "gnubby", "001",
                            device::mojom::kPageFido, kUsageFidoU2f);
  device_added_loop.Run();

  // 3. Create the HidChooserController. The blocked device should be included.
  auto hid_chooser_controller = CreateHidChooserController({});
  options_initialized_loop.Run();

  EXPECT_EQ(1u, hid_chooser_controller->NumOptions());
  EXPECT_EQ(u"gnubby", hid_chooser_controller->GetOption(0));
}

TEST_F(HidChooserControllerTest, AddNamedDevice) {
  base::RunLoop device_added_loop;
  EXPECT_CALL(device_observer(), OnDeviceAdded(_))
      .WillOnce(RunClosure(device_added_loop.QuitClosure()));

  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  // 1. Connect a device with a non-empty product name string.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], 1, 1, "a", "001");
  device_added_loop.Run();

  // 2. Create the HidChooserController. The option text should include the
  // product name.
  auto hid_chooser_controller = CreateHidChooserController({});
  options_initialized_loop.Run();

  EXPECT_EQ(1u, hid_chooser_controller->NumOptions());
  EXPECT_EQ(u"a", hid_chooser_controller->GetOption(0));
}

TEST_F(HidChooserControllerTest, AddUnnamedDevice) {
  base::RunLoop device_added_loop;
  EXPECT_CALL(device_observer(), OnDeviceAdded(_))
      .WillOnce(RunClosure(device_added_loop.QuitClosure()));

  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  // 1. Connect a device with an empty product name string.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], 1, 1, "", "001");
  device_added_loop.Run();

  // 2. Create the HidChooserController. The option text should use an alternate
  // string.
  auto hid_chooser_controller = CreateHidChooserController({});
  options_initialized_loop.Run();

  EXPECT_EQ(1u, hid_chooser_controller->NumOptions());
  EXPECT_EQ(u"Unknown Device (0001:0001)",
            hid_chooser_controller->GetOption(0));
}

TEST_F(HidChooserControllerTest, DeviceIdFilterVendorOnly) {
  base::RunLoop device_added_loop1;
  base::RunLoop device_added_loop2;
  base::RunLoop device_added_loop3;
  EXPECT_CALL(device_observer(), OnDeviceAdded(_))
      .WillOnce(RunClosure(device_added_loop1.QuitClosure()))
      .WillOnce(RunClosure(device_added_loop2.QuitClosure()))
      .WillOnce(RunClosure(device_added_loop3.QuitClosure()));

  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  // 1. Connect a device.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], 1, 1, "a", "001");
  device_added_loop1.Run();

  // 2. Connect a second device with the same vendor ID.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[1], 1, 2, "b", "002");
  device_added_loop2.Run();

  // 3. Connect a device with a different vendor ID.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[2], 2, 2, "c", "003");
  device_added_loop3.Run();

  // 4. Create the HidChooserController with a vendor ID filter. The third
  // device should be excluded.
  std::vector<blink::mojom::HidDeviceFilterPtr> filters;
  filters.push_back(
      blink::mojom::HidDeviceFilter::New(CreateVendorFilter(1), nullptr));
  auto hid_chooser_controller = CreateHidChooserController(std::move(filters));
  options_initialized_loop.Run();

  EXPECT_EQ(2u, hid_chooser_controller->NumOptions());

  std::set<std::u16string> options{hid_chooser_controller->GetOption(0),
                                   hid_chooser_controller->GetOption(1)};
  EXPECT_THAT(options, testing::UnorderedElementsAre(u"a", u"b"));
}

TEST_F(HidChooserControllerTest, DeviceIdFilterVendorAndProduct) {
  base::RunLoop device_added_loop1;
  base::RunLoop device_added_loop2;
  base::RunLoop device_added_loop3;
  EXPECT_CALL(device_observer(), OnDeviceAdded(_))
      .WillOnce(RunClosure(device_added_loop1.QuitClosure()))
      .WillOnce(RunClosure(device_added_loop2.QuitClosure()))
      .WillOnce(RunClosure(device_added_loop3.QuitClosure()));

  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  // 1. Connect a device.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], 1, 1, "a", "001");
  device_added_loop1.Run();

  // 2. Connect a device with matching vendor ID but non-matching product ID.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[1], 1, 2, "b", "002");
  device_added_loop2.Run();

  // 3. Connect a device with non-matching vendor and product IDs.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[2], 2, 2, "c", "003");
  device_added_loop3.Run();

  // 4. Create the HidChooserController with a vendor and product ID filter. The
  // second and third devices should be excluded.
  std::vector<blink::mojom::HidDeviceFilterPtr> filters;
  filters.push_back(blink::mojom::HidDeviceFilter::New(
      CreateVendorAndProductFilter(1, 1), nullptr));
  auto hid_chooser_controller = CreateHidChooserController(std::move(filters));
  options_initialized_loop.Run();

  EXPECT_EQ(1u, hid_chooser_controller->NumOptions());
  EXPECT_EQ(u"a", hid_chooser_controller->GetOption(0));
}

TEST_F(HidChooserControllerTest, UsageFilterUsagePageOnly) {
  base::RunLoop device_added_loop1;
  base::RunLoop device_added_loop2;
  EXPECT_CALL(device_observer(), OnDeviceAdded(_))
      .WillOnce(RunClosure(device_added_loop1.QuitClosure()))
      .WillOnce(RunClosure(device_added_loop2.QuitClosure()));

  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  // 1. Connect a device.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], 1, 1, "a", "001",
                            device::mojom::kPageGenericDesktop,
                            device::mojom::kGenericDesktopGamePad);
  device_added_loop1.Run();

  // 2. Connect a device with a different top-level usage page.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[1], 2, 2, "b", "002",
                            device::mojom::kPageSimulation, 5);
  device_added_loop2.Run();

  // 3. Create the HidChooserController with a usage page filter. The second
  // device should be excluded.
  std::vector<blink::mojom::HidDeviceFilterPtr> filters;
  filters.push_back(blink::mojom::HidDeviceFilter::New(
      nullptr, CreatePageFilter(device::mojom::kPageGenericDesktop)));
  auto hid_chooser_controller = CreateHidChooserController(std::move(filters));
  options_initialized_loop.Run();

  EXPECT_EQ(1u, hid_chooser_controller->NumOptions());
  EXPECT_EQ(u"a", hid_chooser_controller->GetOption(0));
}

TEST_F(HidChooserControllerTest, UsageFilterUsageAndPage) {
  base::RunLoop device_added_loop1;
  base::RunLoop device_added_loop2;
  base::RunLoop device_added_loop3;
  EXPECT_CALL(device_observer(), OnDeviceAdded(_))
      .WillOnce(RunClosure(device_added_loop1.QuitClosure()))
      .WillOnce(RunClosure(device_added_loop2.QuitClosure()))
      .WillOnce(RunClosure(device_added_loop3.QuitClosure()));

  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  // 1. Connect a device.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], 1, 1, "a", "001",
                            device::mojom::kPageGenericDesktop,
                            device::mojom::kGenericDesktopGamePad);
  device_added_loop1.Run();

  // 2. Connect a device with matching usage page but non-matching usage.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[1], 2, 2, "b", "002",
                            device::mojom::kPageGenericDesktop,
                            device::mojom::kGenericDesktopKeyboard);
  device_added_loop2.Run();

  // 3. Connect a device with non-matching usage page and usage.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[2], 3, 3, "c", "003",
                            device::mojom::kPageSimulation, 5);
  device_added_loop3.Run();

  // 4. Create the HidChooserController with a usage page and usage filter. The
  // second and third devices should be excluded.
  std::vector<blink::mojom::HidDeviceFilterPtr> filters;
  filters.push_back(blink::mojom::HidDeviceFilter::New(
      nullptr,
      CreateUsageAndPageFilter(device::mojom::kPageGenericDesktop,
                               device::mojom::kGenericDesktopGamePad)));
  auto hid_chooser_controller = CreateHidChooserController(std::move(filters));
  options_initialized_loop.Run();

  EXPECT_EQ(1u, hid_chooser_controller->NumOptions());
  EXPECT_EQ(u"a", hid_chooser_controller->GetOption(0));
}

TEST_F(HidChooserControllerTest, DeviceIdAndUsageFilterIntersection) {
  base::RunLoop device_added_loop1;
  base::RunLoop device_added_loop2;
  base::RunLoop device_added_loop3;
  EXPECT_CALL(device_observer(), OnDeviceAdded(_))
      .WillOnce(RunClosure(device_added_loop1.QuitClosure()))
      .WillOnce(RunClosure(device_added_loop2.QuitClosure()))
      .WillOnce(RunClosure(device_added_loop3.QuitClosure()));

  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  // 1. Connect a device.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], 1, 1, "a", "001",
                            device::mojom::kPageGenericDesktop,
                            device::mojom::kGenericDesktopGamePad);
  device_added_loop1.Run();

  // 2. Connect a device with matching usage page and usage but non-matching
  // vendor and product IDs.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[1], 2, 2, "b", "002",
                            device::mojom::kPageGenericDesktop,
                            device::mojom::kGenericDesktopGamePad);
  device_added_loop2.Run();

  // 3. Connect a device with matching vendor and product IDs but non-matching
  // usage page and usage.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[2], 1, 1, "c", "003",
                            device::mojom::kPageSimulation, 5);
  device_added_loop3.Run();

  // 4. Create the HidChooserController with a filter that tests vendor ID,
  // product ID, usage page, and usage. The second and third devices should be
  // excluded.
  std::vector<blink::mojom::HidDeviceFilterPtr> filters;
  filters.push_back(blink::mojom::HidDeviceFilter::New(
      CreateVendorAndProductFilter(1, 1),
      CreateUsageAndPageFilter(device::mojom::kPageGenericDesktop,
                               device::mojom::kGenericDesktopGamePad)));
  auto hid_chooser_controller = CreateHidChooserController(std::move(filters));
  options_initialized_loop.Run();

  EXPECT_EQ(1u, hid_chooser_controller->NumOptions());
  EXPECT_EQ(u"a", hid_chooser_controller->GetOption(0));
}

TEST_F(HidChooserControllerTest, DeviceIdAndUsageFilterUnion) {
  base::RunLoop device_added_loop1;
  base::RunLoop device_added_loop2;
  base::RunLoop device_added_loop3;
  EXPECT_CALL(device_observer(), OnDeviceAdded(_))
      .WillOnce(RunClosure(device_added_loop1.QuitClosure()))
      .WillOnce(RunClosure(device_added_loop2.QuitClosure()))
      .WillOnce(RunClosure(device_added_loop3.QuitClosure()));

  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  // 1. Connect a device.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], 1, 1, "a", "001",
                            device::mojom::kPageGenericDesktop,
                            device::mojom::kGenericDesktopGamePad);
  device_added_loop1.Run();

  // 2. Connect a device with matching usage page and usage but non-matching
  // vendor and product IDs.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[1], 2, 2, "b", "002",
                            device::mojom::kPageGenericDesktop,
                            device::mojom::kGenericDesktopGamePad);
  device_added_loop2.Run();

  // 3. Connect a device with matching vendor and product IDs but non-matching
  // usage page and usage.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[2], 1, 1, "c", "003",
                            device::mojom::kPageSimulation, 5);
  device_added_loop3.Run();

  // 4. Create the HidChooserController with a vendor/product ID filter and a
  // usage page/usage filter. No devices should be excluded.
  std::vector<blink::mojom::HidDeviceFilterPtr> filters;
  filters.push_back(blink::mojom::HidDeviceFilter::New(
      CreateVendorAndProductFilter(1, 1), nullptr));
  filters.push_back(blink::mojom::HidDeviceFilter::New(
      nullptr,
      CreateUsageAndPageFilter(device::mojom::kPageGenericDesktop,
                               device::mojom::kGenericDesktopGamePad)));
  auto hid_chooser_controller = CreateHidChooserController(std::move(filters));
  options_initialized_loop.Run();

  EXPECT_EQ(3u, hid_chooser_controller->NumOptions());
}

TEST_F(HidChooserControllerTest, OneOptionForSamePhysicalDevice) {
  base::RunLoop device_added_loop1;
  base::RunLoop device_added_loop2;
  EXPECT_CALL(device_observer(), OnDeviceAdded(_))
      .WillOnce(RunClosure(device_added_loop1.QuitClosure()))
      .WillOnce(RunClosure(device_added_loop2.QuitClosure()));

  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  base::MockCallback<content::HidChooser::Callback> callback;
  base::RunLoop callback_loop;
  std::vector<device::mojom::HidDeviceInfoPtr> devices;
  EXPECT_CALL(callback, Run(_))
      .WillOnce([&devices, &callback_loop](
                    std::vector<device::mojom::HidDeviceInfoPtr> d) {
        devices = std::move(d);
        callback_loop.Quit();
      });

  // 1. Connect a device.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], 1, 1, "a", "001",
                            device::mojom::kPageGenericDesktop,
                            device::mojom::kGenericDesktopGamePad);
  device_added_loop1.Run();

  // 2. Connect a second device with the same physical device ID.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], 1, 1, "a", "001",
                            device::mojom::kPageSimulation, 5);
  device_added_loop2.Run();

  // 3. Create the HidChooserController and register a callback to get the
  // returned device list. There should be a single chooser option representing
  // both devices.
  auto hid_chooser_controller = CreateHidChooserController({}, callback.Get());
  options_initialized_loop.Run();

  EXPECT_EQ(1u, hid_chooser_controller->NumOptions());
  EXPECT_EQ(u"a", hid_chooser_controller->GetOption(0));

  // 4. Select the chooser option. The returned device list should include both
  // devices.
  hid_chooser_controller->Select({0});
  callback_loop.Run();

  EXPECT_EQ(2u, devices.size());
  EXPECT_EQ(kTestPhysicalDeviceIds[0], devices[0]->physical_device_id);
  EXPECT_EQ(kTestPhysicalDeviceIds[0], devices[1]->physical_device_id);
  EXPECT_NE(devices[0]->guid, devices[1]->guid);

  // Regression test for https://crbug.com/1069057. Ensure that the
  // set of options is still valid after the callback is run.
  EXPECT_EQ(1u, hid_chooser_controller->NumOptions());
  EXPECT_EQ(u"a", hid_chooser_controller->GetOption(0));
}

TEST_F(HidChooserControllerTest, NoMergeWithDifferentPhysicalDeviceIds) {
  base::RunLoop device_added_loop1;
  base::RunLoop device_added_loop2;
  EXPECT_CALL(device_observer(), OnDeviceAdded(_))
      .WillOnce(RunClosure(device_added_loop1.QuitClosure()))
      .WillOnce(RunClosure(device_added_loop2.QuitClosure()));

  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  // 1. Connect a device.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], 1, 1, "a", "001",
                            device::mojom::kPageGenericDesktop,
                            device::mojom::kGenericDesktopGamePad);
  device_added_loop1.Run();

  // 2. Connect a second device with the same info as the first device except
  // for the physical device ID.
  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[1], 1, 1, "a", "001",
                            device::mojom::kPageGenericDesktop,
                            device::mojom::kGenericDesktopGamePad);
  device_added_loop2.Run();

  // 3. Create the HidChooserController. The devices should have separate
  // chooser options.
  auto hid_chooser_controller = CreateHidChooserController({});
  options_initialized_loop.Run();

  EXPECT_EQ(2u, hid_chooser_controller->NumOptions());
}

TEST_F(HidChooserControllerTest, NoMergeWithEmptyPhysicalDeviceId) {
  base::RunLoop device_added_loop1;
  base::RunLoop device_added_loop2;
  EXPECT_CALL(device_observer(), OnDeviceAdded(_))
      .WillOnce(RunClosure(device_added_loop1.QuitClosure()))
      .WillOnce(RunClosure(device_added_loop2.QuitClosure()));

  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  // 1. Connect a device with an empty physical device ID.
  CreateAndAddFakeHidDevice("", 1, 1, "a", "001",
                            device::mojom::kPageGenericDesktop,
                            device::mojom::kGenericDesktopGamePad);
  device_added_loop1.Run();

  // 2. Connect a second device with an empty physical device ID.
  CreateAndAddFakeHidDevice("", 1, 1, "a", "001",
                            device::mojom::kPageSimulation, 5);
  device_added_loop2.Run();

  // 3. Create the HidChooserController. The devices should have separate
  // chooser options.
  auto hid_chooser_controller = CreateHidChooserController({});
  options_initialized_loop.Run();

  EXPECT_EQ(2u, hid_chooser_controller->NumOptions());
}

TEST_F(HidChooserControllerTest, DeviceConnectAddsOption) {
  EXPECT_CALL(device_observer(), OnDeviceAdded(_));

  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  base::RunLoop option_added_loop;
  EXPECT_CALL(view(), OnOptionAdded(0))
      .WillOnce(RunClosure(option_added_loop.QuitClosure()));

  // 1. Create the HidChooserController.
  auto hid_chooser_controller = CreateHidChooserController({});
  options_initialized_loop.Run();

  EXPECT_EQ(0u, hid_chooser_controller->NumOptions());

  // 2. Connect a device and verify a chooser option is added.
  auto device =
      CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], 1, 1, "a", "001",
                                device::mojom::kPageGenericDesktop,
                                device::mojom::kGenericDesktopGamePad);
  option_added_loop.Run();

  EXPECT_EQ(1u, hid_chooser_controller->NumOptions());
}

TEST_F(HidChooserControllerTest, DeviceDisconnectRemovesOption) {
  base::RunLoop device_added_loop;
  EXPECT_CALL(device_observer(), OnDeviceAdded(_))
      .WillOnce(RunClosure(device_added_loop.QuitClosure()));

  EXPECT_CALL(device_observer(), OnDeviceRemoved(_));

  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized())
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));

  base::RunLoop option_removed_loop;
  EXPECT_CALL(view(), OnOptionRemoved(0))
      .WillOnce(RunClosure(option_removed_loop.QuitClosure()));

  // 1. Connect a device.
  auto device =
      CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], 1, 1, "a", "001",
                                device::mojom::kPageGenericDesktop,
                                device::mojom::kGenericDesktopGamePad);
  device_added_loop.Run();

  // 2. Create the HidChooserController and verify that the device is included.
  auto hid_chooser_controller = CreateHidChooserController({});
  options_initialized_loop.Run();

  EXPECT_EQ(1u, hid_chooser_controller->NumOptions());

  // 3. Disconnect the device and verify the chooser option is removed.
  DisconnectDevice(*device);
  option_removed_loop.Run();

  EXPECT_EQ(0u, hid_chooser_controller->NumOptions());
}

namespace {

device::mojom::HidDeviceInfoPtr CreateDeviceWithOneCollection(
    const std::string& guid) {
  auto device_info = device::mojom::HidDeviceInfo::New();
  device_info->guid = guid;
  device_info->physical_device_id = "physical-device-id";
  device_info->vendor_id = 1;
  device_info->product_id = 1;
  device_info->product_name = "a";
  auto collection = device::mojom::HidCollectionInfo::New();
  collection->usage = device::mojom::HidUsageAndPage::New(1, 1);
  collection->input_reports.push_back(
      device::mojom::HidReportDescription::New());
  device_info->collections.push_back(std::move(collection));
  return device_info;
}

device::mojom::HidDeviceInfoPtr CreateDeviceWithTwoCollections(
    const std::string guid) {
  auto device_info = CreateDeviceWithOneCollection(guid);
  auto collection = device::mojom::HidCollectionInfo::New();
  collection->usage = device::mojom::HidUsageAndPage::New(2, 2);
  collection->output_reports.push_back(
      device::mojom::HidReportDescription::New());
  device_info->collections.push_back(std::move(collection));
  return device_info;
}

}  // namespace

TEST_F(HidChooserControllerTest, DeviceChangeUpdatesDeviceInfo) {
  const char kTestGuid[] = "guid";

  // Connect a partially-initialized device with one collection.
  base::RunLoop device_added_loop;
  EXPECT_CALL(device_observer(), OnDeviceAdded).WillOnce([&](const auto& d) {
    EXPECT_EQ(d.guid, kTestGuid);
    EXPECT_EQ(d.collections.size(), 1u);
    device_added_loop.Quit();
  });
  auto partial_device = CreateDeviceWithOneCollection(kTestGuid);
  ConnectDevice(*partial_device);
  device_added_loop.Run();

  // Create the HidChooserController.
  base::MockCallback<content::HidChooser::Callback> callback;
  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized)
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));
  auto hid_chooser_controller = CreateHidChooserController({}, callback.Get());
  options_initialized_loop.Run();

  // Check that the option is present.
  EXPECT_EQ(hid_chooser_controller->NumOptions(), 1u);
  EXPECT_EQ(u"a", hid_chooser_controller->GetOption(0));

  // Update the device to add another collection.
  base::RunLoop device_changed_loop;
  EXPECT_CALL(device_observer(), OnDeviceChanged).WillOnce([&](const auto& d) {
    EXPECT_EQ(d.guid, kTestGuid);
    EXPECT_EQ(d.collections.size(), 2u);
    device_changed_loop.Quit();
  });
  auto complete_device = CreateDeviceWithTwoCollections(kTestGuid);
  UpdateDevice(*complete_device);
  device_changed_loop.Run();

  // Check that the option is still present and the name has not changed.
  EXPECT_EQ(hid_chooser_controller->NumOptions(), 1u);
  EXPECT_EQ(u"a", hid_chooser_controller->GetOption(0));

  // Select the option and check that only the updated device info is returned.
  base::RunLoop callback_loop;
  EXPECT_CALL(callback, Run).WillOnce([&](auto devices) {
    ASSERT_EQ(devices.size(), 1u);
    EXPECT_EQ(devices[0]->guid, kTestGuid);
    EXPECT_EQ(devices[0]->collections.size(), 2u);
    callback_loop.Quit();
  });
  hid_chooser_controller->Select({0});
  callback_loop.Run();
}

TEST_F(HidChooserControllerTest, ExcludedDeviceChangedToIncludedDevice) {
  const char kTestGuid[] = "guid";

  // Connect a partially-initialized device with one collection.
  base::RunLoop device_added_loop;
  EXPECT_CALL(device_observer(), OnDeviceAdded).WillOnce([&](const auto& d) {
    EXPECT_EQ(d.guid, kTestGuid);
    EXPECT_EQ(d.collections.size(), 1u);
    device_added_loop.Quit();
  });
  auto partial_device = CreateDeviceWithOneCollection(kTestGuid);
  ConnectDevice(*partial_device);
  device_added_loop.Run();

  // Create the HidChooserController. Set a usage page filter that excludes
  // |partial_device|.
  std::vector<blink::mojom::HidDeviceFilterPtr> filters;
  filters.push_back(
      blink::mojom::HidDeviceFilter::New(nullptr, CreatePageFilter(2)));
  base::MockCallback<content::HidChooser::Callback> callback;
  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized)
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));
  auto hid_chooser_controller =
      CreateHidChooserController(std::move(filters), callback.Get());
  options_initialized_loop.Run();

  // Check that the option is not present.
  EXPECT_EQ(hid_chooser_controller->NumOptions(), 0u);

  // Update the device to add another collection. Now the device should be
  // allowed by the filter.
  base::RunLoop option_added_loop;
  EXPECT_CALL(view(), OnOptionAdded(0))
      .WillOnce(RunClosure(option_added_loop.QuitClosure()));
  EXPECT_CALL(device_observer(), OnDeviceChanged).WillOnce([&](const auto& d) {
    EXPECT_EQ(d.guid, kTestGuid);
    EXPECT_EQ(d.collections.size(), 2u);
  });
  auto complete_device = CreateDeviceWithTwoCollections(kTestGuid);
  UpdateDevice(*complete_device);
  option_added_loop.Run();

  // Check that the option has been added.
  EXPECT_EQ(hid_chooser_controller->NumOptions(), 1u);
  EXPECT_EQ(u"a", hid_chooser_controller->GetOption(0));

  // Select the option and check that only the updated device info is returned.
  base::RunLoop callback_loop;
  EXPECT_CALL(callback, Run).WillOnce([&](auto devices) {
    ASSERT_EQ(devices.size(), 1u);
    EXPECT_EQ(devices[0]->guid, kTestGuid);
    EXPECT_EQ(devices[0]->collections.size(), 2u);
    callback_loop.Quit();
  });
  hid_chooser_controller->Select({0});
  callback_loop.Run();
}

TEST_F(HidChooserControllerTest, DeviceChangedToBlockedDevice) {
  const char kTestGuid[] = "guid";

  // Connect a partially-initialized device with one collection.
  base::RunLoop device_added_loop;
  EXPECT_CALL(device_observer(), OnDeviceAdded).WillOnce([&](const auto& d) {
    EXPECT_EQ(d.guid, kTestGuid);
    EXPECT_EQ(d.collections.size(), 1u);
    device_added_loop.Quit();
  });
  auto partial_device = CreateDeviceWithOneCollection(kTestGuid);
  ConnectDevice(*partial_device);
  device_added_loop.Run();

  // Create the HidChooserController.
  base::RunLoop options_initialized_loop;
  EXPECT_CALL(view(), OnOptionsInitialized)
      .WillOnce(RunClosure(options_initialized_loop.QuitClosure()));
  auto hid_chooser_controller = CreateHidChooserController({});
  options_initialized_loop.Run();

  // Check that the option is present.
  EXPECT_EQ(hid_chooser_controller->NumOptions(), 1u);
  EXPECT_EQ(u"a", hid_chooser_controller->GetOption(0));

  // Update the device to add another collection with the FIDO usage page.
  // Devices with any FIDO collection are blocked, so the chooser option should
  // be removed.
  base::RunLoop option_removed_loop;
  EXPECT_CALL(view(), OnOptionRemoved(0))
      .WillOnce(RunClosure(option_removed_loop.QuitClosure()));
  EXPECT_CALL(device_observer(), OnDeviceChanged).WillOnce([&](const auto& d) {
    EXPECT_EQ(d.guid, kTestGuid);
    EXPECT_EQ(d.collections.size(), 2u);
  });
  auto complete_device = CreateDeviceWithTwoCollections(kTestGuid);
  complete_device->collections[1]->usage->usage_page = device::mojom::kPageFido;
  UpdateDevice(*complete_device);
  option_removed_loop.Run();

  // Check that the option has been removed.
  EXPECT_EQ(hid_chooser_controller->NumOptions(), 0u);
}


text:
package com.sprogcoder.example.database;

import java.util.HashMap;
import java.util.Map;

import android.content.ContentValues;

import com.sprogcoder.grogdb.GrogTable;

public class TableOption extends GrogTable
{

	private static final String TABLE_NAME = "Options";
	private static Map<String, String> columns = new HashMap<String, String>();

	static
	{
		columns.put("OPTION_ID", "TEXT");
		columns.put("INTEGER_VALUE", "INTEGER");
	}

	public TableOption()
	{
		super(TABLE_NAME, columns);
	}

	public void setBooleanOption(String optionID, boolean onoffBoolean)
	{
		int onoff = (onoffBoolean) ? 1 : 0;
		ContentValues cv = new ContentValues();
		cv.put("OPTION_ID", optionID);
		cv.put("INTEGER_VALUE", onoff);
		updateOrInsertRow(TABLE_NAME, cv, "OPTION_ID", optionID);
	}

	public boolean getBooleanOption(String optionID)
	{
		String args = "OPTION_ID='" + optionID + "'";
		return getBoolean(TABLE_NAME, args, "INTEGER_VALUE");
	}

}