text:
<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 21819 -->
<!-- Reviewed: yes -->
<sect1 id="learning.quickstart.create-model">
    <title>Utworzenie modelu oraz tabeli w bazie danych</title>

    <para>
        Przed rozpoczęciem należy zastanowić się nad następującym zagadnieniem: gdzie tworzone
        klasy będą przechowywane i w jaki sposób będzie można je odnaleźć? Utworzony właśnie
        domyślny projekt, używa automatycznego dołączania plików (autoloader). Możliwe jest
        dołączenie do niego kolejnych autoloaderów tak by umożliwić odnajdywanie tworzonych
        klas. Typowym rozwiązaniem jest umieszczenie różnych klas w głównym katalogu - w tym
        wypadku nazwanego <filename>application/</filename> - i nazywanie ich z zachowaniem
        wspólnego prefiksu.
    </para>

    <para>
        Klasa <classname>Zend_Controller_Front</classname> umożliwia tworzenie modułów (modules)
        - odrębnych części, które same w sobie są mini-aplikacjami. W ramach każdego
        modułu odwzorowywana jest taka sama struktura katalogów jaka jest tworzona przez
        narzędzie <command>zf</command> w katalogu głównym aplikacji. Nazwy wszystkich
        klas w jednym module muszą rozpoczynać się od wspólnego prefiksu - nazwy modułu.
        Katalog główny - <filename>application/</filename> również jest modułem (domyślnym)
        dlatego też jego zasoby zostaną uwzględnione w procesie automatycznego dołączania plików.
    </para>

    <para>
        <classname>Zend_Application_Module_Autoloader</classname> oferuje funkcjonalność
        niezbędną do odwzorowania zasobów modułów na odpowiednie ścieżki katalogów oraz
        ułatwia zachowanie spójnego standardu nazewnictwa. Instancja tej klasy jest tworzona
        domyślnie podczas uruchamiania klasy bootstrap. Domyślnym prefiksem używanym przez
        bootstrap jest "Application" więc modele, formularze oraz klasy tabel będą rozpoczynały
        się prefiksem "Application_".
    </para>

    <para>
        Teraz należy się zastanowić co składa się na księgę gości. Typowo będzie w niej
        lista wpisów z <emphasis>komentarzem</emphasis>, <emphasis>czasem zapisu</emphasis>
        oraz <emphasis>adresem email</emphasis>. Zakładając użycie bazy danych, pole
        <emphasis>unikalny identyfikator</emphasis> może również być przydatne. Aplikacja
        powinna umożliwiać zapis danych, pobieranie wpisów pojedynczo oraz wszystkich na raz.
        Prosty model oferujący opisaną funkcjonalność może przedstawiać się następująco:
    </para>

    <programlisting language="php"><![CDATA[
// application/models/Guestbook.php

class Application_Model_Guestbook
{
    protected $_comment;
    protected $_created;
    protected $_email;
    protected $_id;

    public function __set($name, $value);
    public function __get($name);

    public function setComment($text);
    public function getComment();

    public function setEmail($email);
    public function getEmail();

    public function setCreated($ts);
    public function getCreated();

    public function setId($id);
    public function getId();
}

class Application_Model_GuestbookMapper
{
    public function save(Application_Model_Guestbook $guestbook);
    public function find($id);
    public function fetchAll();
}
]]></programlisting>

    <para>
        Metody <methodname>__get()</methodname> oraz <methodname>__set()</methodname>
        stanowią mechanizmy ułatwiające dostęp do poszczególnych właściwości oraz
        pośredniczą w dostępie do innych getterów i setterów. Dzięki nim, można
        również upewnić się, że jedynie pożądane właściwości będą dostępne.
    </para>

    <para>
        Metody <methodname>find()</methodname> oraz <methodname>fetchAll()</methodname> umożliwiają
        zwrócenie pojedynczego lub wszystkich rekordów natomiast <methodname>save()</methodname>
        zapisuje dane.
    </para>

    <para>
        W tym momencie można zacząć myśleć o skonfigurowaniu bazy danych.
    </para>

    <para>
        Na początku należy zainicjować zasób <classname>Db</classname>. Jego konfiguracja
        jest możliwa na podobnych zasadach jak w przypadku zasobów
        <classname>Layout</classname> oraz <classname>View</classname>.
        Można to osiągnąć za pomocą polecenia <command>zf configure db-adapter</command>:
    </para>

    <programlisting language="shell"><![CDATA[
% zf configure db-adapter \
> 'adapter=PDO_SQLITE&dbname=APPLICATION_PATH "/../data/db/guestbook.db"' \
> production
A db configuration for the production has been written to the application config file.

% zf configure db-adapter \
> 'adapter=PDO_SQLITE&dbname=APPLICATION_PATH "/../data/db/guestbook-testing.db"' \
> testing
A db configuration for the production has been written to the application config file.

% zf configure db-adapter \
> 'adapter=PDO_SQLITE&dbname=APPLICATION_PATH "/../data/db/guestbook-dev.db"' \
> development
A db configuration for the production has been written to the application config file.
]]></programlisting>

    <para>
        Teraz, po otworzeniu pliku <filename>application/configs/application.ini</filename>
        można zobaczyć instrukcje, jakie zostały do niego dodane w odpowiednich sekcjach.
    </para>

    <programlisting language="ini"><![CDATA[
; application/configs/application.ini

[production]
; ...
resources.db.adapter = "PDO_SQLITE"
resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook.db"

[testing : production]
; ...
resources.db.adapter = "PDO_SQLITE"
resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db"

[development : production]
; ...
resources.db.adapter = "PDO_SQLITE"
resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
]]></programlisting>

    <para>
        Ostatecznie plik konfiguracyjny powinien wyglądać następująco:
    </para>

    <programlisting language="ini"><![CDATA[
; application/configs/application.ini

[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
appnamespace = "Application"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.frontController.params.displayExceptions = 0
resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
resources.view[] =
resources.db.adapter = "PDO_SQLITE"
resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook.db"

[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.db.adapter = "PDO_SQLITE"
resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db"

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.db.adapter = "PDO_SQLITE"
resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
]]></programlisting>

    <para>
        Należy zauważyć, iż baza (bazy) danych będzie przechowywana w katalogu
        <filename>data/db/</filename>.
        Te katalogi powinny zostać utworzone i udostępnione wszystkim. W systemach
        unix można tego dokonać następująco:
    </para>

    <programlisting language="shell"><![CDATA[
% mkdir -p data/db; chmod -R a+rwX data
]]></programlisting>

    <para>
        W systemach Windows należy utworzyć odpowiednie katalogi w eksploratorze
        oraz ustawić uprawnienia w taki sposób aby każdy użytkownik miał prawo zapisu.
    </para>

    <para>
        Połączenie z bazą danych zostało utworzone - w tym przypadku bazą jest Sqlite
        znajdująca się w katalogu <filename>application/data/</filename>. Następnym krokiem jest
        utworzenie tabeli przechowującej rekordy księgi gości.
    </para>

    <programlisting language="sql"><![CDATA[
-- scripts/schema.sqlite.sql
--
-- Poniższy kod SQL należy uruchomić w bazie danych

CREATE TABLE guestbook (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    email VARCHAR(32) NOT NULL DEFAULT 'noemail@test.com',
    comment TEXT NULL,
    created DATETIME NOT NULL
);

CREATE INDEX "id" ON "guestbook" ("id");
]]></programlisting>

    <para>
        Aby mieć zestaw danych do testowania można utworzyć w tabeli kilka rekordów.
    </para>

    <programlisting language="sql"><![CDATA[
-- scripts/data.sqlite.sql
--
-- Poniższy kod SQL może posłużyć do zapełnienia tabeli testowymi danymi

INSERT INTO guestbook (email, comment, created) VALUES
    ('ralph.schindler@zend.com',
    'Hello! Hope you enjoy this sample zf application!',
    DATETIME('NOW'));
INSERT INTO guestbook (email, comment, created) VALUES
    ('foo@bar.com',
    'Baz baz baz, baz baz Baz baz baz - baz baz baz.',
    DATETIME('NOW'));
]]></programlisting>

    <para>
        Mamy zdefiniowany schemat bazy danych oraz niewielką ilość danych do zaimportowania.
        Teraz można napisać skrypt tworzący bazę danych. Ten krok nie jest potrzebny w środowisku
        produkcyjnym. Dzięki niemu można lokalnie wypracować odpowiednią strukturę bazy danych
        aby tworzona aplikacja działała zgodnie z założeniami. Skrypt
        <filename>scripts/load.sqlite.php</filename> można wypełnić w poniższy sposób:
    </para>

    <programlisting language="php"><![CDATA[
// scripts/load.sqlite.php

/**
 * Skrypt tworzący bazę danych i wypełniający ją danymi
 */

// Inicjalizacja ścieżek oraz autoloadera
defined('APPLICATION_PATH')
    || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));
set_include_path(implode(PATH_SEPARATOR, array(
    APPLICATION_PATH . '/../library',
    get_include_path(),
)));
require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance();

// Zdefiniowanie opcji CLI
$getopt = new Zend_Console_Getopt(array(
    'withdata|w' => 'Load database with sample data',
    'env|e-s'    => 'Application environment for which to create database (defaults to development)',
    'help|h'     => 'Help -- usage message',
));
try {
    $getopt->parse();
} catch (Zend_Console_Getopt_Exception $e) {
    // Bad options passed: report usage
    echo $e->getUsageMessage();
    return false;
}

// W przypadku zażądania pomocy, wyświetlenie informacji o użyciu
if ($getopt->getOption('h')) {
    echo $getopt->getUsageMessage();
    return true;
}

// Inicjalizacja wartości na podstawie opcji CLI
$withData = $getopt->getOption('w');
$env      = $getopt->getOption('e');
defined('APPLICATION_ENV')
    || define('APPLICATION_ENV', (null === $env) ? 'development' : $env);

// Inicjalizacja Zend_Application
$application = new Zend_Application(
    APPLICATION_ENV,
    APPLICATION_PATH . '/configs/application.ini'
);

// Inicjalizacja oraz zwrócenie zasobu bazy danych
$bootstrap = $application->getBootstrap();
$bootstrap->bootstrap('db');
$dbAdapter = $bootstrap->getResource('db');

// Powiadomienie użytkownika o postępie (w tym miejscu jest tworzona baza danych)
if ('testing' != APPLICATION_ENV) {
    echo 'Writing Database Guestbook in (control-c to cancel): ' . PHP_EOL;
    for ($x = 5; $x > 0; $x--) {
        echo $x . "\r"; sleep(1);
    }
}

// Sprawdzenie czy plik bazy danych istnieje
$options = $bootstrap->getOption('resources');
$dbFile  = $options['db']['params']['dbname'];
if (file_exists($dbFile)) {
    unlink($dbFile);
}

// Wywołanie poleceń zawartych w pliku tworzącym schemat
try {
    $schemaSql = file_get_contents(dirname(__FILE__) . '/schema.sqlite.sql');
    // bezpośrednie użycie obiektu połączenia w celu wywołania poleceń SQL
    $dbAdapter->getConnection()->exec($schemaSql);
    chmod($dbFile, 0666);

    if ('testing' != APPLICATION_ENV) {
        echo PHP_EOL;
        echo 'Database Created';
        echo PHP_EOL;
    }

    if ($withData) {
        $dataSql = file_get_contents(dirname(__FILE__) . '/data.sqlite.sql');
        // bezpośrednie użycie obiektu połączenia w celu wywołania poleceń SQL
        $dbAdapter->getConnection()->exec($dataSql);
        if ('testing' != APPLICATION_ENV) {
            echo 'Data Loaded.';
            echo PHP_EOL;
        }
    }

} catch (Exception $e) {
    echo 'AN ERROR HAS OCCURED:' . PHP_EOL;
    echo $e->getMessage() . PHP_EOL;
    return false;
}

// Ten skrypt powinien zostać uruchomiony z wiersza poleceń
return true;
]]></programlisting>

    <para>
        Teraz należy wywołać powyższy skrypt. Można to zrobić z poziomu terminala lub
        wiersza poleceń poprzez wpisanie następującej komendy:
    </para>

    <programlisting language="shell"><![CDATA[
% php scripts/load.sqlite.php --withdata
]]></programlisting>

    <para>
        Powinien pojawić się następujący komunikat:
    </para>

    <programlisting language="text"><![CDATA[
path/to/ZendFrameworkQuickstart/scripts$ php load.sqlite.php --withdata
Writing Database Guestbook in (control-c to cancel):
1
Database Created
Data Loaded.
]]></programlisting>

    <para>
        Po zdefiniowaniu bazy danych aplikacji księgi gości można przystąpić do budowy kodu
        samej aplikacji. W następnych krokach zostanie zbudowana klasa dostępu do danych
        (poprzez <classname>Zend_Db_Table</classname>), oraz klasa mapująca - służąca do
        połączenia z wcześniej opisanym modelem.
        Na koniec utworzony zostanie kontroler zarządzający modelem, którego zadaniem będzie
        wyświetlanie istniejących rekordów oraz obróbka nowych danych.
    </para>

    <para>
        Aby łączyć się ze źródłem danych użyty zostanie wzorzec
        <ulink url="http://martinfowler.com/eaaCatalog/tableDataGateway.html">Table Data
        Gateway</ulink> udostępniany poprzez klasę <classname>Zend_Db_Table</classname>.
        Na początek należy utworzyć klasę opartą o  <classname>Zend_Db_Table</classname>.
        Podobnie jak przy layoucie oraz adapterze bazy danych - można skorzystać z narzędzia
        <command>zf</command> i jego komendy <command>create db-table</command>. Należy przy tym
        podać minimalnie dwa argumenty: nazwę tworzonej klasy oraz nazwę tabeli bazy danych,
        do której prowadzi.
    </para>

    <programlisting language="shell"><![CDATA[
% zf create db-table Guestbook guestbook
Creating a DbTable at application/models/DbTable/Guestbook.php
Updating project profile 'zfproject.xml'
]]></programlisting>

    <para>
        Spoglądając na strukturę katalogów należy zwrócić uwagę na nowy katalog
        <filename>application/models/DbTable/</filename> zawierający plik
        <filename>Guestbook.php</filename>. Ten plik powinien zawierać następującą
        treść:
    </para>

    <programlisting language="php"><![CDATA[
// application/models/DbTable/Guestbook.php

/**
 * This is the DbTable class for the guestbook table.
 */
class Application_Model_DbTable_Guestbook extends Zend_Db_Table_Abstract
{
    /** Table name */
    protected $_name    = 'guestbook';
}
]]></programlisting>

    <para>
        Należy zwrócić uwagę na prefiks: <classname>Application_Model_DbTable</classname>.
        Prefiks klas aplikacji "Application" znajduje się na pierwszym miejscu. Po nim
        występuje komponent "Model_DbTable", który jest mapowany do katalogu
        <filename>models/DbTable/</filename> znajdującego się w module.
    </para>

    <para>
        Jedyne dane niezbędne przy tworzeniu klasy pochodnej w stosunku do
        <classname>Zend_Db_Table</classname> to nazwa tabeli i opcjonalnie klucz pierwotny
        (jeśli jest inny niż "id").
    </para>

    <para>
        Teraz należy utworzyć klasę mapującą obiekt w aplikacji na obiekt w bazie danych czyli
        <ulink url="http://martinfowler.com/eaaCatalog/dataMapper.html">Data Mapper</ulink>.
        Obiektem w bazie danych jest <classname>Application_Model_Guestbook</classname> natomiast
        za obiekt bazy danych odpowiada <classname>Application_Model_DbTable_Guestbook</classname>.
        Typowe <acronym>API</acronym> takiej klasy wygląda następująco:
    </para>

    <programlisting language="php"><![CDATA[
// application/models/GuestbookMapper.php

class Application_Model_GuestbookMapper
{
    public function save($model);
    public function find($id, $model);
    public function fetchAll();
}
]]></programlisting>

    <para>
        Dodatkowo można zdefiniować metody ustawiające i zwracające Table Data Gateway.
        Do utworzenia klasy można użyć narzędzia <command>zf</command>:
    </para>

    <programlisting language="shell"><![CDATA[
% zf create model GuestbookMapper
Creating a model at application/models/GuestbookMapper.php
Updating project profile '.zfproject.xml'
]]></programlisting>

    <para>
        Po otwarciu klasy <classname>Application_Model_GuestbookMapper</classname> z lokalizacji
        <filename>application/models/GuestbookMapper.php</filename> widać następujący kod:
    </para>

    <programlisting language="php"><![CDATA[
// application/models/GuestbookMapper.php

class Application_Model_GuestbookMapper
{
    protected $_dbTable;

    public function setDbTable($dbTable)
    {
        if (is_string($dbTable)) {
            $dbTable = new $dbTable();
        }
        if (!$dbTable instanceof Zend_Db_Table_Abstract) {
            throw new Exception('Invalid table data gateway provided');
        }
        $this->_dbTable = $dbTable;
        return $this;
    }

    public function getDbTable()
    {
        if (null === $this->_dbTable) {
            $this->setDbTable('Application_Model_DbTable_Guestbook');
        }
        return $this->_dbTable;
    }

    public function save(Application_Model_Guestbook $guestbook)
    {
        $data = array(
            'email'   => $guestbook->getEmail(),
            'comment' => $guestbook->getComment(),
            'created' => date('Y-m-d H:i:s'),
        );

        if (null === ($id = $guestbook->getId())) {
            unset($data['id']);
            $this->getDbTable()->insert($data);
        } else {
            $this->getDbTable()->update($data, array('id = ?' => $id));
        }
    }

    public function find($id, Application_Model_Guestbook $guestbook)
    {
        $result = $this->getDbTable()->find($id);
        if (0 == count($result)) {
            return;
        }
        $row = $result->current();
        $guestbook->setId($row->id)
                  ->setEmail($row->email)
                  ->setComment($row->comment)
                  ->setCreated($row->created);
    }

    public function fetchAll()
    {
        $resultSet = $this->getDbTable()->fetchAll();
        $entries   = array();
        foreach ($resultSet as $row) {
            $entry = new Application_Model_Guestbook();
            $entry->setId($row->id)
                  ->setEmail($row->email)
                  ->setComment($row->comment)
                  ->setCreated($row->created);
            $entries[] = $entry;
        }
        return $entries;
    }
}
]]></programlisting>

    <para>
        W obecnym momencie można przystąpić do utworzenia klasy modelu używając
        polecenia <command>zf create model</command>:
    </para>

    <programlisting language="shell"><![CDATA[
% zf create model Guestbook
Creating a model at application/models/Guestbook.php
Updating project profile '.zfproject.xml'
]]></programlisting>

    <para>
        Nowo utworzoną klasę <acronym>PHP</acronym> można zmodyfikować tak aby
        ułatwić umieszczanie danych
        w modelu poprzez przekazanie tablicy do konstruktora lub do metody
        <methodname>setOptions()</methodname>. Ostatecznie model znajdujący się w
        <filename>application/models/Guestbook.php</filename> powinien wyglądać następująco:
    </para>

    <programlisting language="php"><![CDATA[
// application/models/Guestbook.php

class Application_Model_Guestbook
{
    protected $_comment;
    protected $_created;
    protected $_email;
    protected $_id;

    public function __construct(array $options = null)
    {
        if (is_array($options)) {
            $this->setOptions($options);
        }
    }

    public function __set($name, $value)
    {
        $method = 'set' . $name;
        if (('mapper' == $name) || !method_exists($this, $method)) {
            throw new Exception('Invalid guestbook property');
        }
        $this->$method($value);
    }

    public function __get($name)
    {
        $method = 'get' . $name;
        if (('mapper' == $name) || !method_exists($this, $method)) {
            throw new Exception('Invalid guestbook property');
        }
        return $this->$method();
    }

    public function setOptions(array $options)
    {
        $methods = get_class_methods($this);
        foreach ($options as $key => $value) {
            $method = 'set' . ucfirst($key);
            if (in_array($method, $methods)) {
                $this->$method($value);
            }
        }
        return $this;
    }

    public function setComment($text)
    {
        $this->_comment = (string) $text;
        return $this;
    }

    public function getComment()
    {
        return $this->_comment;
    }

    public function setEmail($email)
    {
        $this->_email = (string) $email;
        return $this;
    }

    public function getEmail()
    {
        return $this->_email;
    }

    public function setCreated($ts)
    {
        $this->_created = $ts;
        return $this;
    }

    public function getCreated()
    {
        return $this->_created;
    }

    public function setId($id)
    {
        $this->_id = (int) $id;
        return $this;
    }

    public function getId()
    {
        return $this->_id;
    }
}
]]></programlisting>

    <para>
        W ostatnim kroku, aby połączyć wszystkie elementy, należy utworzyć kontroler,
        którego zadaniem będzie zaprezentowanie listy zapisanych rekordów oraz obsługa
        dodawania nowych danych.
    </para>

    <para>
        Aby to osiągnąć należy użyć polecenia <command>zf create controller</command>:
    </para>

    <programlisting language="shell"><![CDATA[
% zf create controller Guestbook
Creating a controller at
    application/controllers/GuestbookController.php
Creating an index action method in controller Guestbook
Creating a view script for the index action method at
    application/views/scripts/guestbook/index.phtml
Creating a controller test file at
    tests/application/controllers/GuestbookControllerTest.php
Updating project profile '.zfproject.xml'
]]></programlisting>

    <para>
        Powyższe polecenie tworzy nowy kontroler - <classname>GuestbookController</classname>
        w pliku <filename>application/controllers/GuestbookController.php</filename> zawierający
        jedną akcję - <methodname>indexAction()</methodname>.
        Na użytek tego kontrolera utworzony zostaje również katalog widoków:
        <filename>application/views/scripts/guestbook/</filename> zawierający skrypt
        widoku dla akcji index.
    </para>

    <para>
        Akcja "index" będzie stanowić domyślny punkt kontrolera pokazujący zapisane rekordy.
    </para>

    <para>
        Teraz należy zaprogramować logikę aplikacji. Aby pokazać zapisane rekordy użytkownikowi
        wchodzącemu do <methodname>indexAction()</methodname> można użyć poniższego kodu:
    </para>

    <programlisting language="php"><![CDATA[
// application/controllers/GuestbookController.php

class GuestbookController extends Zend_Controller_Action
{
    public function indexAction()
    {
        $guestbook = new Application_Model_GuestbookMapper();
        $this->view->entries = $guestbook->fetchAll();
    }
}
]]></programlisting>

    <para>
        Dodatkowo potrzebny jest jeszcze widok wyświetlający dane. W pliku
        <filename>application/views/scripts/guestbook/index.phtml</filename> można umieścić
        następujący zapis:
    </para>

    <programlisting language="php"><![CDATA[
<!-- application/views/scripts/guestbook/index.phtml -->

<p><a href="<?php echo $this->url(
    array(
        'controller' => 'guestbook',
        'action'     => 'sign'
    ),
    'default',
    true) ?>">Sign Our Guestbook</a></p>

Guestbook Entries: <br />
<dl>
    <?php foreach ($this->entries as $entry): ?>
    <dt><?php echo $this->escape($entry->email) ?></dt>
    <dd><?php echo $this->escape($entry->comment) ?></dd>
    <?php endforeach ?>
</dl>
]]></programlisting>

    <note>
        <title>Punkt kontrolny</title>

        <para>
            Teraz, po przejściu do "http://localhost/guestbook" powinna się pojawić lista
            zapisanych rekordów:
        </para>

        <para>
            <inlinegraphic width="525" scale="100" align="center" valign="middle"
                fileref="figures/learning.quickstart.create-model.png" format="PNG" />
        </para>
    </note>

    <note>
        <title>Użycie skryptu ładującego dane</title>

        <para>
            Skrypt ładujący dane pokazany we wcześniejszej części tego rozdziału
            (<filename>scripts/load.sqlite.php</filename>) może zostać użyty do utworzenia
            bazy danych jak i do zaimportowania przykładowych danych dla każdego środowiska.
            Wewnętrznie korzysta z klasy <classname>Zend_Console_Getopt</classname>, dzięki
            czemu możliwe jest podanie parametrów sterujących skryptem. Podając parametr
            "-h" lub "--help" można zapoznać się z dostępnymi opcjami:
        </para>

        <programlisting language="php"><![CDATA[
Usage: load.sqlite.php [ options ]
--withdata|-w         Load database with sample data
--env|-e [  ]         Application environment for which to create database
                      (defaults to development)
--help|-h             Help -- usage message)]]
]]></programlisting>

        <para>
            Parametr "-e" pozwala na nadanie wartości stałej <constant>APPLICATION_ENV</constant>
            określającej środowisko, co z kolei umożliwia utworzenie bazy danych SQLite dla
            każdego środowiska oddzielnie. Należy się upewnić, że skrypt jest uruchamiany z
            odpowiednią wartością tego parametru dla każdego ze środowisk.
        </para>
    </note>
</sect1>


text:
import React, { Component } from 'react';
import { observer } from 'mobx-react';

import { bem } from 'app/libs/bem';

import Header from 'app/modules/layout/components/Header';
import Footer from 'app/modules/layout/components/Footer';

const b = bem('DefaultLayout');

@observer
export default class DefaultLayout extends Component {
    render() {
        const {
            children
        } = this.props;

        return (
            <div className={b()}>
                <Header />
                <main className={b('wrapper')}>
                    {children}
                </main>
                <Footer />
            </div>
        );
    }
}

redpajama_set_name: RedPajamaGithub


text:
#ifndef QPID_AMQP_0_10_ARRAY_H
#define QPID_AMQP_0_10_ARRAY_H

#include "qpid/amqp_0_10/TypeForCode.h"
#include "qpid/amqp_0_10/CodeForType.h"
#include "qpid/amqp_0_10/UnknownType.h"
#include "qpid/amqp_0_10/exceptions.h"
#include "qpid/amqp_0_10/Codec.h"
#include <vector>
#include <ostream>

namespace qpid {
namespace amqp_0_10 {

template <class T> class  ArrayDomain : public std::vector<T>  {
  public:
    template <class S> void serialize(S& s) { s.split(*this); }

    template <class S> void encode(S& s) const {
        s(contentSize())(CodeForType<T>::value)(uint32_t(this->size()));
        s(this->begin(), this->end());
    }

    void encode(Codec::Size& s) const  { s.raw(0, contentSize() + 4/*size*/); }

    template <class S> void decode(S& s) {
        uint32_t size; uint8_t type; uint32_t count;
        s(size);
        typename S::ScopedLimit l(s, size);
        s(type);
        if (type != CodeForType<T>::value)
            throw InvalidArgumentException(QPID_MSG("Array domain expected type " << CodeForType<T>::value << " but found " << type));
        s(count);
        this->resize(count);
        s(this->begin(), this->end());
    }

  private:
    uint32_t contentSize() const {
        return Codec::size(this->begin(), this->end()) + sizeof(uint32_t) /*count*/ + sizeof(uint8_t) /*type*/;
    }
};

template <class T>
std::ostream& operator<<(std::ostream& o, const ArrayDomain<T>& ad) {
    std::ostream_iterator<T> i(o, " ");
    o << "Array<" << typeName(CodeForType<T>::value) << ">[";
    std::copy(ad.begin(), ad.end(), i);
    o << "]";
    return o;
}

/** A non-domain array is represented as and array of UnknownType.
 * Special case templat.
 */
template<> class ArrayDomain<UnknownType> : public std::vector<UnknownType> {
  public:
    ArrayDomain(uint8_t type_=0) : type(type_) {}

    template <class S> void serialize(S& s) { s.split(*this); }

    template <class S> void encode(S& s) const {
        s(contentSize())(type)(uint32_t(this->size()));
        s(this->begin(), this->end());
    }

    void encode(Codec::Size& s) const  { s.raw(0, contentSize() + 4/*size*/); }

    template <class S> void decode(S& s) {
        uint32_t size; uint32_t count;
        s(size);
        typename S::ScopedLimit l(s, size);
        s(type)(count);
        this->clear();
        this->resize(count, UnknownType(type));
        s(this->begin(), this->end());
    }

    uint8_t getType() const { return type; }

  private:
    uint32_t contentSize() const {
        return Codec::size(this->begin(), this->end()) + sizeof(uint32_t) /*count*/ + sizeof(uint8_t) /*type*/;
    }
    uint8_t type;
};

std::ostream& operator<<(std::ostream& o, const Array& a);

// FIXME aconway 2008-04-08: hack to supress encoding of
// command-fragments and in-doubt as there is a problem with the spec
// (command-fragments does not have a one byte type code.)
namespace session { class CommandFragment; }
namespace dtx { class Xid; }

template <> struct ArrayDomain<session::CommandFragment> : public Void {};
template <> struct ArrayDomain<dtx::Xid> : public Void {};
inline std::ostream& operator<<(std::ostream& o, const ArrayDomain<session::CommandFragment>&) { return o; }
inline std::ostream& operator<<(std::ostream& o, const ArrayDomain<dtx::Xid>&) { return o; }

}} // namespace qpid::amqp_0_10

#endif  /*!QPID_AMQP_0_10_ARRAY_H*/


text:
package edu.algorithms.unit;

import edu.algorithms.Configuration;
import edu.algorithms.ConfigurationFileNotFoundException;

import org.junit.Before;
import org.junit.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;

public class ConfigurationTest {

    private Configuration configuration;

    @Before
    public void setUp()
            throws Exception {
        configuration = new Configuration.Builder("target").build();
    }

    @Test
    public void testBuildConfiguration()
            throws Exception {
        assertThat(configuration, is(notNullValue()));
    }

    @Test(expected = ConfigurationFileNotFoundException.class)
    public void testBuildConfigurationWithWrongPath_shouldThrowException()
            throws Exception {
        new Configuration.Builder("path").build();
    }

    @Test
    public void testDefaultProperties()
            throws Exception {
        final double linkClickRate = configuration.getProperty("link.click.rate");
        final double directPageRate = configuration.getProperty("direct.page.rate");
        assertThat(linkClickRate + directPageRate, is(1.0));
    }
}


text:
package behavioral

import (
	"bytes"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestTalk(t *testing.T) {
	bufferOutputWriter := outputWriter
	outputWriter = new(bytes.Buffer)
	defer func() { outputWriter = bufferOutputWriter }()

	// Fred will "Execute" Barney which will result in a call to PassOn()
	// Barney will "Execute" Betty which will result in a call to Gossip()
	// Betty will "Execute" Wilma which will result in a call to Listen()
	wilma := NewPerson("Wilma", NewCommand(nil, nil))
	betty := NewPerson("Betty", NewCommand(&wilma, wilma.Listen))
	barney := NewPerson("Barney", NewCommand(&betty, betty.Gossip))
	fred := NewPerson("Fred", NewCommand(&barney, barney.PassOn))
	fred.Talk()

	assert.Equal(t, "Fred is talking.\n"+
		"Barney is passing on.\n"+
		"Betty is gossiping.\n"+
		"Wilma is listening.\n", outputWriter.(*bytes.Buffer).String())
}


text:
/*
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
/*
 * This code was generated by https://github.com/googleapis/google-api-java-client-services/
 * Modify at your own risk.
 */

package com.google.api.services.videointelligence.v1.model;

/**
 * Config for LABEL_DETECTION.
 *
 *
 This is the Java data model class that specifies how to parse/serialize into the JSON that is
 * transmitted over HTTP when working with the Cloud Video Intelligence API. For a detailed
 * explanation see:
 * https://developers.google.com/api-client-library/java/google-http-java-client/json
 *

 *
 * @author Google, Inc.
 */
@SuppressWarnings("javadoc")
public final class GoogleCloudVideointelligenceV1LabelDetectionConfig extends com.google.api.client.json.GenericJson {

  /**
   * The confidence threshold we perform filtering on the labels from frame-level detection. If not
   * set, it is set to 0.4 by default. The valid range for this threshold is [0.1, 0.9]. Any value
   * set outside of this range will be clipped. Note: For best results, follow the default
   * threshold. We will update the default threshold everytime when we release a new model.
   * The value may be {@code null}.
   */
  @com.google.api.client.util.Key
  private java.lang.Float frameConfidenceThreshold;

  /**
   * What labels should be detected with LABEL_DETECTION, in addition to video-level labels or
   * segment-level labels. If unspecified, defaults to `SHOT_MODE`.
   * The value may be {@code null}.
   */
  @com.google.api.client.util.Key
  private java.lang.String labelDetectionMode;

  /**
   * Model to use for label detection. Supported values: "builtin/stable" (the default if unset) and
   * "builtin/latest".
   * The value may be {@code null}.
   */
  @com.google.api.client.util.Key
  private java.lang.String model;

  /**
   * Whether the video has been shot from a stationary (i.e., non-moving) camera. When set to true,
   * might improve detection accuracy for moving objects. Should be used with `SHOT_AND_FRAME_MODE`
   * enabled.
   * The value may be {@code null}.
   */
  @com.google.api.client.util.Key
  private java.lang.Boolean stationaryCamera;

  /**
   * The confidence threshold we perform filtering on the labels from video-level and shot-level
   * detections. If not set, it's set to 0.3 by default. The valid range for this threshold is [0.1,
   * 0.9]. Any value set outside of this range will be clipped. Note: For best results, follow the
   * default threshold. We will update the default threshold everytime when we release a new model.
   * The value may be {@code null}.
   */
  @com.google.api.client.util.Key
  private java.lang.Float videoConfidenceThreshold;

  /**
   * The confidence threshold we perform filtering on the labels from frame-level detection. If not
   * set, it is set to 0.4 by default. The valid range for this threshold is [0.1, 0.9]. Any value
   * set outside of this range will be clipped. Note: For best results, follow the default
   * threshold. We will update the default threshold everytime when we release a new model.
   * @return value or {@code null} for none
   */
  public java.lang.Float getFrameConfidenceThreshold() {
    return frameConfidenceThreshold;
  }

  /**
   * The confidence threshold we perform filtering on the labels from frame-level detection. If not
   * set, it is set to 0.4 by default. The valid range for this threshold is [0.1, 0.9]. Any value
   * set outside of this range will be clipped. Note: For best results, follow the default
   * threshold. We will update the default threshold everytime when we release a new model.
   * @param frameConfidenceThreshold frameConfidenceThreshold or {@code null} for none
   */
  public GoogleCloudVideointelligenceV1LabelDetectionConfig setFrameConfidenceThreshold(java.lang.Float frameConfidenceThreshold) {
    this.frameConfidenceThreshold = frameConfidenceThreshold;
    return this;
  }

  /**
   * What labels should be detected with LABEL_DETECTION, in addition to video-level labels or
   * segment-level labels. If unspecified, defaults to `SHOT_MODE`.
   * @return value or {@code null} for none
   */
  public java.lang.String getLabelDetectionMode() {
    return labelDetectionMode;
  }

  /**
   * What labels should be detected with LABEL_DETECTION, in addition to video-level labels or
   * segment-level labels. If unspecified, defaults to `SHOT_MODE`.
   * @param labelDetectionMode labelDetectionMode or {@code null} for none
   */
  public GoogleCloudVideointelligenceV1LabelDetectionConfig setLabelDetectionMode(java.lang.String labelDetectionMode) {
    this.labelDetectionMode = labelDetectionMode;
    return this;
  }

  /**
   * Model to use for label detection. Supported values: "builtin/stable" (the default if unset) and
   * "builtin/latest".
   * @return value or {@code null} for none
   */
  public java.lang.String getModel() {
    return model;
  }

  /**
   * Model to use for label detection. Supported values: "builtin/stable" (the default if unset) and
   * "builtin/latest".
   * @param model model or {@code null} for none
   */
  public GoogleCloudVideointelligenceV1LabelDetectionConfig setModel(java.lang.String model) {
    this.model = model;
    return this;
  }

  /**
   * Whether the video has been shot from a stationary (i.e., non-moving) camera. When set to true,
   * might improve detection accuracy for moving objects. Should be used with `SHOT_AND_FRAME_MODE`
   * enabled.
   * @return value or {@code null} for none
   */
  public java.lang.Boolean getStationaryCamera() {
    return stationaryCamera;
  }

  /**
   * Whether the video has been shot from a stationary (i.e., non-moving) camera. When set to true,
   * might improve detection accuracy for moving objects. Should be used with `SHOT_AND_FRAME_MODE`
   * enabled.
   * @param stationaryCamera stationaryCamera or {@code null} for none
   */
  public GoogleCloudVideointelligenceV1LabelDetectionConfig setStationaryCamera(java.lang.Boolean stationaryCamera) {
    this.stationaryCamera = stationaryCamera;
    return this;
  }

  /**
   * The confidence threshold we perform filtering on the labels from video-level and shot-level
   * detections. If not set, it's set to 0.3 by default. The valid range for this threshold is [0.1,
   * 0.9]. Any value set outside of this range will be clipped. Note: For best results, follow the
   * default threshold. We will update the default threshold everytime when we release a new model.
   * @return value or {@code null} for none
   */
  public java.lang.Float getVideoConfidenceThreshold() {
    return videoConfidenceThreshold;
  }

  /**
   * The confidence threshold we perform filtering on the labels from video-level and shot-level
   * detections. If not set, it's set to 0.3 by default. The valid range for this threshold is [0.1,
   * 0.9]. Any value set outside of this range will be clipped. Note: For best results, follow the
   * default threshold. We will update the default threshold everytime when we release a new model.
   * @param videoConfidenceThreshold videoConfidenceThreshold or {@code null} for none
   */
  public GoogleCloudVideointelligenceV1LabelDetectionConfig setVideoConfidenceThreshold(java.lang.Float videoConfidenceThreshold) {
    this.videoConfidenceThreshold = videoConfidenceThreshold;
    return this;
  }

  @Override
  public GoogleCloudVideointelligenceV1LabelDetectionConfig set(String fieldName, Object value) {
    return (GoogleCloudVideointelligenceV1LabelDetectionConfig) super.set(fieldName, value);
  }

  @Override
  public GoogleCloudVideointelligenceV1LabelDetectionConfig clone() {
    return (GoogleCloudVideointelligenceV1LabelDetectionConfig) super.clone();
  }

}


text:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DreamNucleus.Heos.Infrastructure.Heos
{
    public class LimitList<T> : IEnumerable<T>
    {
        private LinkedList<T> _list = new LinkedList<T>();

        public LimitList(int maximumCount)
        {
            if (maximumCount <= 0)
                throw new ArgumentException(null, "maximumCount");

            MaximumCount = maximumCount;
        }

        public int MaximumCount { get; private set; }

        public int Count
        {
            get
            {
                return _list.Count;
            }
        }

        public void Add(T value)
        {
            if (_list.Count == MaximumCount)
            {
                _list.RemoveFirst();
            }
            _list.AddLast(value);
        }

        public T this[int index]
        {
            get
            {
                if (index < 0 || index >= Count)
                    throw new ArgumentOutOfRangeException();

                return _list.Skip(index).First();
            }
        }

        public IEnumerator<T> GetEnumerator()
        {
            return _list.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}


text:
/*
 * Helper method for urllib to fetch the proxy configuration settings
 * using the SystemConfiguration framework.
 */
#include <Python.h>
#include <SystemConfiguration/SystemConfiguration.h>

static int32_t
cfnum_to_int32(CFNumberRef num)
{
    int32_t result;

    CFNumberGetValue(num, kCFNumberSInt32Type, &result);
    return result;
}

static PyObject*
cfstring_to_pystring(CFStringRef ref)
{
    const char* s;

    s = CFStringGetCStringPtr(ref, kCFStringEncodingUTF8);
    if (s) {
        return PyUnicode_DecodeUTF8(
                        s, strlen(s), NULL);

    } else {
        CFIndex len = CFStringGetLength(ref);
        Boolean ok;
        PyObject* result;
        char* buf;

        buf = PyMem_Malloc(len*4);
        if (buf == NULL) {
            PyErr_NoMemory();
            return NULL;
        }

        ok = CFStringGetCString(ref,
                        buf, len * 4,
                        kCFStringEncodingUTF8);
        if (!ok) {
            PyMem_Free(buf);
            return NULL;
        } else {
            result = PyUnicode_DecodeUTF8(
                            buf, strlen(buf), NULL);
            PyMem_Free(buf);
        }
        return result;
    }
}

static PyObject*
get_proxy_settings(PyObject* mod __attribute__((__unused__)))
{
    CFDictionaryRef proxyDict = NULL;
    CFNumberRef aNum = NULL;
    CFArrayRef anArray = NULL;
    PyObject* result = NULL;
    PyObject* v;
    int r;

    Py_BEGIN_ALLOW_THREADS
    proxyDict = SCDynamicStoreCopyProxies(NULL);
    Py_END_ALLOW_THREADS

    if (!proxyDict) {
        Py_RETURN_NONE;
    }

    result = PyDict_New();
    if (result == NULL) goto error;

    aNum = CFDictionaryGetValue(proxyDict,
        kSCPropNetProxiesExcludeSimpleHostnames);
    if (aNum == NULL) {
        v = PyBool_FromLong(0);
    } else {
        v = PyBool_FromLong(cfnum_to_int32(aNum));
    }

    if (v == NULL) goto error;

    r = PyDict_SetItemString(result, "exclude_simple", v);
    Py_DECREF(v); v = NULL;
    if (r == -1) goto error;

    anArray = CFDictionaryGetValue(proxyDict,
                    kSCPropNetProxiesExceptionsList);
    if (anArray != NULL) {
        CFIndex len = CFArrayGetCount(anArray);
        CFIndex i;
        v = PyTuple_New(len);
        if (v == NULL) goto error;

        r = PyDict_SetItemString(result, "exceptions", v);
        Py_DECREF(v);
        if (r == -1) goto error;

        for (i = 0; i < len; i++) {
            CFStringRef aString = NULL;

            aString = CFArrayGetValueAtIndex(anArray, i);
            if (aString == NULL) {
                PyTuple_SetItem(v, i, Py_None);
                Py_INCREF(Py_None);
            } else {
                PyObject* t = cfstring_to_pystring(aString);
                if (!t) {
                    PyTuple_SetItem(v, i, Py_None);
                    Py_INCREF(Py_None);
                } else {
                    PyTuple_SetItem(v, i, t);
                }
            }
        }
    }

    CFRelease(proxyDict);
    return result;

error:
    if (proxyDict)  CFRelease(proxyDict);
    Py_XDECREF(result);
    return NULL;
}

static int
set_proxy(PyObject* proxies, const char* proto, CFDictionaryRef proxyDict,
                CFStringRef enabledKey,
                CFStringRef hostKey, CFStringRef portKey)
{
    CFNumberRef aNum;

    aNum = CFDictionaryGetValue(proxyDict, enabledKey);
    if (aNum && cfnum_to_int32(aNum)) {
        CFStringRef hostString;

        hostString = CFDictionaryGetValue(proxyDict, hostKey);
        aNum = CFDictionaryGetValue(proxyDict, portKey);

        if (hostString) {
            int r;
            PyObject* h = cfstring_to_pystring(hostString);
            PyObject* v;
            if (h) {
                if (aNum) {
                    int32_t port = cfnum_to_int32(aNum);
                    v = PyUnicode_FromFormat("http://%U:%ld",
                        h, (long)port);
                } else {
                    v = PyUnicode_FromFormat("http://%U", h);
                }
                Py_DECREF(h);
                if (!v) return -1;
                r = PyDict_SetItemString(proxies, proto,
                    v);
                Py_DECREF(v);
                return r;
            }
        }

    }
    return 0;
}

static PyObject*
get_proxies(PyObject* mod __attribute__((__unused__)))
{
    PyObject* result = NULL;
    int r;
    CFDictionaryRef proxyDict = NULL;

    Py_BEGIN_ALLOW_THREADS
    proxyDict = SCDynamicStoreCopyProxies(NULL);
    Py_END_ALLOW_THREADS

    if (proxyDict == NULL) {
        return PyDict_New();
    }

    result = PyDict_New();
    if (result == NULL) goto error;

    r = set_proxy(result, "http", proxyDict,
        kSCPropNetProxiesHTTPEnable,
        kSCPropNetProxiesHTTPProxy,
        kSCPropNetProxiesHTTPPort);
    if (r == -1) goto error;
    r = set_proxy(result, "https", proxyDict,
        kSCPropNetProxiesHTTPSEnable,
        kSCPropNetProxiesHTTPSProxy,
        kSCPropNetProxiesHTTPSPort);
    if (r == -1) goto error;
    r = set_proxy(result, "ftp", proxyDict,
        kSCPropNetProxiesFTPEnable,
        kSCPropNetProxiesFTPProxy,
        kSCPropNetProxiesFTPPort);
    if (r == -1) goto error;
    r = set_proxy(result, "gopher", proxyDict,
        kSCPropNetProxiesGopherEnable,
        kSCPropNetProxiesGopherProxy,
        kSCPropNetProxiesGopherPort);
    if (r == -1) goto error;

    CFRelease(proxyDict);
    return result;
error:
    if (proxyDict)  CFRelease(proxyDict);
    Py_XDECREF(result);
    return NULL;
}

static PyMethodDef mod_methods[] = {
    {
        "_get_proxy_settings",
        (PyCFunction)get_proxy_settings,
        METH_NOARGS,
        NULL,
    },
    {
        "_get_proxies",
        (PyCFunction)get_proxies,
        METH_NOARGS,
        NULL,
    },
    { 0, 0, 0, 0 }
};

static struct PyModuleDef mod_module = {
    PyModuleDef_HEAD_INIT,
    "_scproxy",
    NULL,
    -1,
    mod_methods,
    NULL,
    NULL,
    NULL,
    NULL
};

#ifdef __cplusplus
extern "C" {
#endif

PyMODINIT_FUNC
PyInit__scproxy(void)
{
    return PyModule_Create(&mod_module);
}

#ifdef __cplusplus
}
#endif


text:
require 'active_record'
require File::expand_path('../../database', __FILE__)

module DataTransit

  module Target

    class TargetBase < ActiveRecord::Base
      self.abstract_class = true
      establish_connection DataTransit::Database.target
    end
  end

end


text:
/**
 * Author: chenboxiang
 * Date: 14-4-29
 * Time: 下午6:00
 */
'use strict';

var assert = require('../lib/assert');
var should = require('should');

describe('assert', function() {
    describe('.equal(obj1, obj2, message)', function() {
        it('should throw err when obj1 is not strict equal to obj2', function() {
            (function() {
                assert.equal(1, 2, 'msg')
            }).should.throw();
        })

        it('should not throw err when obj1 is strict equal to obj2', function() {
            assert.equal(1, 1, 'msg');
        })
    })

    describe('.isTrue(obj, message)', function() {
        it('should throw err when obj is not strict equal to true', function() {
            (function() {
                assert.isTrue(false, 'msg')
            }).should.throw();
        })

        it('should not throw err when obj is strict equal to true', function() {
            assert.isTrue(true, 'msg');
        })
    })

    describe('.notEmpty(obj, message)', function() {
        it('should throw err when obj is empty', function() {
            (function() {
                assert.notEmpty('', 'msg')
            }).should.throw();
            (function() {
                assert.notEmpty(null, 'msg')
            }).should.throw();
            (function() {
                assert.notEmpty([], 'msg')
            }).should.throw();
            (function() {
                assert.notEmpty({}, 'msg')
            }).should.throw();
        })

        it('should not throw err when obj is not empty', function() {
            assert.notEmpty('1', 'msg');
        })
    })

    describe('.notNull(obj, message)', function() {
        it('should throw err when obj is null or undefined', function() {
            (function() {
                assert.notNull(null, 'msg')
            }).should.throw();
            (function() {
                assert.notNull(void 0, 'msg')
            }).should.throw();
        })

        it('should not throw err when obj is not empty', function() {
            assert.notNull('1', 'msg');
        })
    })
})