Mojo-SQLite-3.008000755001750001750 014150724762 13600 5ustar00grinnzgrinnz000000000000README100644001750001750 3704714150724762 14574 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008NAME Mojo::SQLite - A tiny Mojolicious wrapper for SQLite SYNOPSIS use Mojo::SQLite; # Select the library version my $sql = Mojo::SQLite->new('sqlite:test.db'); say $sql->db->query('select sqlite_version() as version')->hash->{version}; # Use migrations to create a table $sql->migrations->name('my_names_app')->from_string(<migrate; -- 1 up create table names (id integer primary key autoincrement, name text); -- 1 down drop table names; EOF # Use migrations to drop and recreate the table $sql->migrations->migrate(0)->migrate; # Get a database handle from the cache for multiple queries my $db = $sql->db; # Use SQL::Abstract to generate simple CRUD queries for you $db->insert('names', {name => 'Isabel'}); my $id = $db->select('names', ['id'], {name => 'Isabel'})->hash->{id}; $db->update('names', {name => 'Bel'}, {id => $id}); $db->delete('names', {name => 'Bel'}); # Insert a few rows in a transaction with SQL and placeholders eval { my $tx = $db->begin; $db->query('insert into names (name) values (?)', 'Sara'); $db->query('insert into names (name) values (?)', 'Stefan'); $tx->commit; }; say $@ if $@; # Insert another row with SQL::Abstract and return the generated id say $db->insert('names', {name => 'Daniel'})->last_insert_id; # JSON roundtrip say $db->query('select ? as foo', {json => {bar => 'baz'}}) ->expand(json => 'foo')->hash->{foo}{bar}; # Select one row at a time my $results = $db->query('select * from names'); while (my $next = $results->hash) { say $next->{name}; } # Select all rows with SQL::Abstract say $_->{name} for $db->select('names')->hashes->each; DESCRIPTION Mojo::SQLite is a tiny wrapper around DBD::SQLite that makes SQLite a lot of fun to use with the Mojolicious real-time web framework. Use all SQL features SQLite has to offer, generate CRUD queries from data structures, and manage your database schema with migrations. BASICS Database and statement handles are cached automatically, so they can be reused transparently to increase performance. And you can handle connection timeouts gracefully by holding on to them only for short amounts of time. use Mojolicious::Lite; use Mojo::SQLite; helper sqlite => sub { state $sql = Mojo::SQLite->new('sqlite:test.db') }; get '/' => sub ($c) { my $db = $c->sqlite->db; $c->render(json => $db->query(q{select datetime('now','localtime') as now})->hash); }; app->start; In this example application, we create a sqlite helper to store a Mojo::SQLite object. Our action calls that helper and uses the method "db" in Mojo::SQLite to dequeue a Mojo::SQLite::Database object from the connection pool. Then we use the method "query" in Mojo::SQLite::Database to execute an SQL statement, which returns a Mojo::SQLite::Results object. And finally we call the method "hash" in Mojo::SQLite::Results to retrieve the first row as a hash reference. All I/O and queries are performed synchronously, and SQLite's default journal mode only supports concurrent reads from multiple processes while the database is not being written. The "Write-Ahead Log" journal mode allows multiple processes to read and write concurrently to the same database file (but only one can write at a time). WAL mode is enabled by the wal_mode option, currently enabled by default, and persists when opening that same database in the future. # Performed concurrently (concurrent with writing only with WAL journaling mode) my $pid = fork || die $!; say $sql->db->query(q{select datetime('now','localtime') as time})->hash->{time}; exit unless $pid; The no_wal option prevents WAL mode from being enabled in new databases but doesn't affect databases where it has already been enabled. wal_mode may not be set by default in a future release. See http://sqlite.org/wal.html and "journal_mode" in DBD::SQLite for more information. The double-quoted string literal misfeature is disabled for all connections since Mojo::SQLite 3.003; use single quotes for string literals and double quotes for identifiers, as is normally recommended. All cached database handles will be reset automatically if a new process has been forked, this allows multiple processes to share the same Mojo::SQLite object safely. Any database errors will throw an exception as RaiseError is automatically enabled, so use eval or Try::Tiny to catch them. This makes transactions with "begin" in Mojo::SQLite::Database easy. While passing a file path of :memory: (or a custom "dsn" with mode=memory) will create a temporary database, in-memory databases cannot be shared between connections, so subsequent calls to "db" may return connections to completely different databases. For a temporary database that can be shared between connections and processes, pass a file path of :temp: to store the database in a temporary directory (this is the default), or consider constructing a temporary directory yourself with File::Temp if you need to reuse the filename. A temporary directory allows SQLite to create additional temporary files safely. use File::Spec::Functions 'catfile'; use File::Temp; use Mojo::SQLite; my $tempdir = File::Temp->newdir; # Deleted when object goes out of scope my $tempfile = catfile $tempdir, 'test.db'; my $sql = Mojo::SQLite->new->from_filename($tempfile); EXAMPLES This distribution also contains a well-structured example blog application you can use for inspiration. This application shows how to apply the MVC design pattern in practice. EVENTS Mojo::SQLite inherits all events from Mojo::EventEmitter and can emit the following new ones. connection $sql->on(connection => sub ($sql, $dbh) { $dbh->do('pragma journal_size_limit=1000000'); }); Emitted when a new database connection has been established. ATTRIBUTES Mojo::SQLite implements the following attributes. abstract my $abstract = $sql->abstract; $sql = $sql->abstract(SQL::Abstract->new); SQL::Abstract object used to generate CRUD queries for Mojo::SQLite::Database, defaults to a SQL::Abstract::Pg object with name_sep set to . and quote_char set to ". # Generate WHERE clause and bind values my($stmt, @bind) = $sql->abstract->where({foo => 'bar', baz => 'yada'}); SQL::Abstract::Pg provides additional features to the SQL::Abstract query methods in Mojo::SQLite::Database such as -json and limit/offset. The for feature is not applicable to SQLite queries. $sql->db->select(['some_table', ['other_table', foo_id => 'id']], ['foo', [bar => 'baz'], \q{datetime('now') as dt}], {foo => 'value'}, {order_by => 'foo', limit => 10, offset => 5, group_by => ['foo'], having => {baz => 'value'}}); # Upsert supported since SQLite 3.24.0 $sql->db->insert('some_table', {name => $name, value => $value}, {on_conflict => [name => {value => \'"excluded"."value"'}]}); auto_migrate my $bool = $sql->auto_migrate; $sql = $sql->auto_migrate($bool); Automatically migrate to the latest database schema with "migrations", as soon as "db" has been called for the first time. database_class my $class = $sql->database_class; $sql = $sql->database_class('MyApp::Database'); Class to be used by "db", defaults to Mojo::SQLite::Database. Note that this class needs to have already been loaded before "db" is called. dsn my $dsn = $sql->dsn; $sql = $sql->dsn('dbi:SQLite:uri=file:foo.db'); Data source name, defaults to dbi:SQLite:dbname= followed by a path to a temporary file. max_connections my $max = $sql->max_connections; $sql = $sql->max_connections(3); Maximum number of idle database handles to cache for future use, defaults to 1. migrations my $migrations = $sql->migrations; $sql = $sql->migrations(Mojo::SQLite::Migrations->new); Mojo::SQLite::Migrations object you can use to change your database schema more easily. # Load migrations from file and migrate to latest version $sql->migrations->from_file('/home/dbook/migrations.sql')->migrate; options my $options = $sql->options; $sql = $sql->options({AutoCommit => 1, RaiseError => 1}); Options for database handles, defaults to setting sqlite_string_mode to DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK, setting AutoCommit, AutoInactiveDestroy and RaiseError, and deactivating PrintError. Note that AutoCommit and RaiseError are considered mandatory, so deactivating them would be very dangerous. See "ATTRIBUTES COMMON TO ALL HANDLES" in DBI and "DRIVER PRIVATE ATTRIBUTES" in DBD::SQLite for more information on available options. parent my $parent = $sql->parent; $sql = $sql->parent(Mojo::SQLite->new); Another Mojo::SQLite object to use for connection management, instead of establishing and caching our own database connections. METHODS Mojo::SQLite inherits all methods from Mojo::EventEmitter and implements the following new ones. new my $sql = Mojo::SQLite->new; my $sql = Mojo::SQLite->new('file:test.db); my $sql = Mojo::SQLite->new('sqlite:test.db'); my $sql = Mojo::SQLite->new(Mojo::SQLite->new); Construct a new Mojo::SQLite object and parse connection string with "from_string" if necessary. # Customize configuration further my $sql = Mojo::SQLite->new->dsn('dbi:SQLite:dbname=test.db'); my $sql = Mojo::SQLite->new->dsn('dbi:SQLite:uri=file:test.db?mode=memory'); # Pass filename directly my $sql = Mojo::SQLite->new->from_filename($filename); db my $db = $sql->db; Get a database object based on "database_class" (which is usually Mojo::SQLite::Database) for a cached or newly established database connection. The DBD::SQLite database handle will be automatically cached again when that object is destroyed, so you can handle problems like connection timeouts gracefully by holding on to it only for short amounts of time. # Add up all the money say $sql->db->select('accounts') ->hashes->reduce(sub { $a->{money} + $b->{money} }); from_filename $sql = $sql->from_filename('C:\\Documents and Settings\\foo & bar.db', $options); Parse database filename directly. Unlike "from_string", the filename is parsed as a local filename and not a URL. A hashref of "options" may be passed as the second argument. # Absolute filename $sql->from_filename('/home/fred/data.db'); # Relative to current directory $sql->from_filename('data.db'); # Temporary file database (default) $sql->from_filename(':temp:'); # In-memory temporary database (single connection only) my $db = $sql->from_filename(':memory:')->db; # Additional options $sql->from_filename($filename, { PrintError => 1 }); # Readonly connection without WAL mode $sql->from_filename($filename, { ReadOnly => 1, no_wal => 1 }); # Strict unicode strings and WAL mode use DBD::SQLite::Constants ':dbd_sqlite_string_mode'; $sql->from_filename($filename, { sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_STRICT, wal_mode => 1 }); from_string $sql = $sql->from_string('test.db'); $sql = $sql->from_string('file:test.db'); $sql = $sql->from_string('file:///C:/foo/bar.db'); $sql = $sql->from_string('sqlite:C:%5Cfoo%5Cbar.db'); $sql = $sql->from_string(Mojo::SQLite->new); Parse configuration from connection string or use another Mojo::SQLite object as "parent". Connection strings are parsed as URLs, so you should construct them using a module like Mojo::URL, URI::file, or URI::db. For portability on non-Unix-like systems, either construct the URL with the sqlite scheme, or use "new" in URI::file to construct a URL with the file scheme. A URL with no scheme will be parsed as a file URL, and file URLs are parsed according to the current operating system. If specified, the hostname must be localhost. If the URL has a query string, it will be parsed and applied to "options". # Absolute filename $sql->from_string('sqlite:////home/fred/data.db'); $sql->from_string('sqlite://localhost//home/fred/data.db'); $sql->from_string('sqlite:/home/fred/data.db'); $sql->from_string('file:///home/fred/data.db'); $sql->from_string('file://localhost/home/fred/data.db'); $sql->from_string('file:/home/fred/data.db'); $sql->from_string('///home/fred/data.db'); $sql->from_string('//localhost/home/fred/data.db'); $sql->from_string('/home/fred/data.db'); # Relative to current directory $sql->from_string('sqlite:data.db'); $sql->from_string('file:data.db'); $sql->from_string('data.db'); # Connection string must be a valid URL $sql->from_string(Mojo::URL->new->scheme('sqlite')->path($filename)); $sql->from_string(URI::db->new->Mojo::Base::tap(engine => 'sqlite')->Mojo::Base::tap(dbname => $filename)); $sql->from_string(URI::file->new($filename)); # Temporary file database (default) $sql->from_string(':temp:'); # In-memory temporary database (single connection only) my $db = $sql->from_string(':memory:')->db; # Additional options $sql->from_string('data.db?PrintError=1&sqlite_allow_multiple_statements=1'); $sql->from_string(Mojo::URL->new->scheme('sqlite')->path($filename)->query(sqlite_see_if_its_a_number => 1)); $sql->from_string(URI::file->new($filename)->Mojo::Base::tap(query_form => {PrintError => 1})); # Readonly connection without WAL mode $sql->from_string('data.db?ReadOnly=1&no_wal=1'); # String unicode strings and WAL mode use DBD::SQLite::Constants ':dbd_sqlite_string_mode'; $sql->from_string(Mojo::URL->new->scheme('sqlite')->path('data.db') ->query(sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_STRICT, wal_mode => 1)); DEBUGGING You can set the DBI_TRACE environment variable to get some advanced diagnostics information printed by DBI. DBI_TRACE=1 DBI_TRACE=15 DBI_TRACE=SQL REFERENCE This is the class hierarchy of the Mojo::SQLite distribution. * Mojo::SQLite * Mojo::SQLite::Database * Mojo::SQLite::Migrations * Mojo::SQLite::Results * Mojo::SQLite::Transaction BUGS Report any issues on the public bugtracker. AUTHOR Dan Book, dbook@cpan.org CREDITS Sebastian Riedel, author of Mojo::Pg, which this distribution is based on. COPYRIGHT AND LICENSE Copyright 2015, Dan Book. This library is free software; you may redistribute it and/or modify it under the terms of the Artistic License version 2.0. SEE ALSO Mojolicious, Mojo::Pg, DBD::SQLite Changes100644001750001750 1767214150724762 15211 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.0083.008 2021-11-28 11:10:24 EST - Update documentation to indicate that upsert operations are now supported via SQL::Abstract::Pg. 3.007 2021-08-01 20:17:42 EDT - Increase DBD::SQLite dependency to 1.68 to support and use sqlite_string_mode over sqlite_unicode (#22, Adam Williamson) - The default options now set sqlite_string_mode to DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK, unless sqlite_unicode is specified explicitly in from_string or from_filename. This is equivalent to the previous behavior but ensures that DBD::SQLite will not return malformed strings. - Support wal_mode option to enable WAL journaling mode, which is still currently the default, but may not be set by default in a future release. 3.006 2021-06-16 00:30:47 EDT - Set default abstract generator to an SQL::Abstract::Pg object, enabling additional features. - Update IRC metadata to libera.chat 3.005 2021-02-15 21:47:59 EST - Add query_p, select_p, insert_p, update_p, delete_p Mojo::Promise-returning methods to Mojo::SQLite::Database. These are for API compatibility with Mojo::Pg and do not provide non-blocking query functionality. (#20, Stefan Adams) - Use Mojo::Promise in tests instead of the deprecated and decored Mojo::IOLoop::Delay (#20, Stefan Adams) 3.004 2020-07-22 22:50:43 EDT - The Mojo::SQLite::Migrations sqlite attribute and the Mojo::SQLite::Transaction db attribute are now weak attributes, increasing the required version of Mojolicious to 8.03. - Defer loading of Mojo::IOLoop unless the callback compatibility API is used. - Newly created mojo_migrations tables from Mojo::SQLite::Migrations will use a primary key. - Recognize -json parameters in queries. 3.003 2019-10-01 15:49:43 EDT - Increase DBD::SQLite dependency to 1.64 to support configuring double-quoted string literals. - Disable double-quoted string literals for all connections. https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted - Change default SQL::Abstract identifier back to double quote. 3.002 2019-06-21 17:41:35 EDT - Changed default SQL::Abstract identifier quoting character to `, as double quoted identifiers may be interpreted as string literals if they are not valid identifiers. https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted 3.001 2018-07-20 20:25:12 EDT - Increase DBD::SQLite dependency to 1.54 for FTS5 support. - Add db attribute to Mojo::SQLite::Results and use it to prevent connections from being cached for reuse while results are active. - Add sql_for method to Mojo::SQLite::Migrations. 3.000 2017-07-20 01:16:50 EDT - Changed default for max_connections attribute to 1. - Added support for sharing the database connection cache between multiple Mojo::SQLite objects. (based on Mojo::Pg 4.0) - Added parent attribute to Mojo::SQLite. - Fixed database connection leak with automatic migrations. - Removed deprecated Mojo::SQLite::PubSub and associated methods and attributes. SQLite's serverless nature means it does not have the ability to support client notifications, so it is not possible to implement an efficient pubsub system as in for example PostgreSQL, Redis, or websockets. 2.002 2017-06-01 14:16:34 EDT - Add no_wal option to prevent enabling WAL mode on connection. 2.001 2017-02-18 15:36:16 EST - Set name_sep in default SQL::Abstract object to support proper quoting of table and column names. 2.000 2017-02-11 17:03:53 EST - Add support for generating queries with SQL::Abstract. (based on Mojo::Pg 3.0) - Add abstract attribute to Mojo::SQLite. - Add delete, insert, select, and update methods to Mojo::SQLite::Database. 1.004 2017-01-17 00:10:51 EST - Use Mojo::File from Mojolicious 7.15 instead of deprecated Mojo::Util slurp function. (#9) 1.003 2016-12-11 16:30:31 EST - Add links to alternatives for deprecated Mojo::SQLite::PubSub. 1.002 2016-11-30 11:17:56 EST - Improved contextual caller information in query error messages. (#6) - Fix memory leak when reusing the same database handle many times. (#7) 1.001 2016-11-15 02:32:27 EST - Deprecate Mojo::SQLite::PubSub and associated methods and attributes. SQLite's serverless nature means it does not have the ability to support client notifications, so it is not possible to implement an efficient pubsub system as in for example PostgreSQL, Redis, or websockets. 1.000 2016-08-17 21:01:31 EDT - Bump version to 1.000 as this distribution is considered stable - Added database_class attribute to Mojo::SQLite and results_class attribute to Mojo::SQLite::Database to make it easier for these classes to be extended - Fixed a few fork-safety bugs and memory leaks in Mojo::SQLite::PubSub 0.022 2016-07-14 16:59:36 EDT - Improve expand performance slightly - Add support for encoding and decoding of JSON notifications - Add json method to Mojo::SQLite::PubSub - Fix bug where poll_interval in Mojo::SQLite::PubSub was not correctly used - Fix case where notifications from other connections could be lost 0.021 2016-02-15 01:58:40 EST - Lower dependency on DBI to 1.627 - Remove dependency on URI::QueryParam 0.020 2016-02-14 17:53:55 EST - Bump dependency on DBD::SQLite to 1.50 for JSON1 extension support - Add support for json parameters to query method in Mojo::SQLite::Database - Add expand method to Mojo::SQLite::Results to decode json columns - Add auto_migrate attribute to Mojo::SQLite (from Mojo::Pg) - Add tables method to Mojo::SQLite::Database (from Mojo::Pg) 0.019 2015-11-25 22:45:25 EST - Added support for removing all subscribers of a channel at once in unlisten method in Mojo::SQLite::PubSub - Mojo::SQLite::Migrations now throws an error if the currently active version is greater than the latest version - Added finish method to Mojo::SQLite::Results 0.018 2015-09-21 13:44:30 EDT - Fixes for tests under win32 0.017 2015-09-20 17:24:44 EDT - Fix parsing of options from connection strings - Allow passing a hashref of options in from_filename 0.016 2015-09-20 16:51:53 EDT - Use URI::db to parse connection strings to allow sqlite: connection strings 0.015 2015-09-12 00:13:53 EDT - Minor pubsub fixes 0.014 2015-09-11 20:25:25 EDT - Add Mojo::SQLite::PubSub and listen/notify implementation in Mojo::SQLite::Database 0.013 2015-09-02 02:44:36 EDT - Cache rowid for successful inserts in Results objects as last_insert_id 0.012 2015-08-31 18:26:47 EDT - Include better context for DB error messages from $db->query and migrations - Switch installer to Module::Build::Tiny 0.011 2015-08-13 20:43:37 EDT - Simplify Results refcounting - Pass undef instead of broken Results object to callback if statement prepare failed 0.010 2015-08-08 22:50:06 EDT - Create a tempdir for temporary databases - Use immediate locking for migrations instead of exclusive - Run query callbacks using next_tick instead of immediately - Don't call finish in Results destructor while other Results objects refer to the statement handle (GH #2) 0.009 2015-08-05 12:28:48 EDT - Simplify some code and examples with Mojo::Base::tap 0.008 2015-08-03 19:23:49 EDT - Use and recommend URI to portably parse filenames into URLs - Add from_filename method 0.007 2015-08-01 15:17:22 EDT - Clarify use of URLs in connection strings 0.006 2015-07-28 23:54:45 EDT - Fix results rows() method to return statement handle rows - Make scheme optional for from_string() (and thus the constructor) 0.005 2015-07-28 21:59:23 EDT - Allow specifying bind types for query parameters 0.004 2015-07-28 20:48:04 EDT - Depend on DBI explicitly - Remove List::Util dependency - Add blog example 0.003 2015-07-28 12:56:30 EDT - Minor migrations fixes 0.002 2015-07-28 12:39:12 EDT - Allow passing callback to query() 0.001 2015-07-28 02:48:42 EDT - First release LICENSE100644001750001750 2151514150724762 14712 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008This software is Copyright (c) 2015 by Dan Book. This is free software, licensed under: The Artistic License 2.0 (GPL Compatible) The Artistic License 2.0 Copyright (c) 2000-2006, The Perl Foundation. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble This license establishes the terms under which a given free software Package may be copied, modified, distributed, and/or redistributed. The intent is that the Copyright Holder maintains some artistic control over the development of that Package while still keeping the Package available as open source and free software. You are always permitted to make arrangements wholly outside of this license directly with the Copyright Holder of a given Package. If the terms of this license do not permit the full use that you propose to make of the Package, you should contact the Copyright Holder and seek a different licensing arrangement. Definitions "Copyright Holder" means the individual(s) or organization(s) named in the copyright notice for the entire Package. "Contributor" means any party that has contributed code or other material to the Package, in accordance with the Copyright Holder's procedures. "You" and "your" means any person who would like to copy, distribute, or modify the Package. "Package" means the collection of files distributed by the Copyright Holder, and derivatives of that collection and/or of those files. A given Package may consist of either the Standard Version, or a Modified Version. "Distribute" means providing a copy of the Package or making it accessible to anyone else, or in the case of a company or organization, to others outside of your company or organization. "Distributor Fee" means any fee that you charge for Distributing this Package or providing support for this Package to another party. It does not mean licensing fees. "Standard Version" refers to the Package if it has not been modified, or has been modified only in ways explicitly requested by the Copyright Holder. "Modified Version" means the Package, if it has been changed, and such changes were not explicitly requested by the Copyright Holder. "Original License" means this Artistic License as Distributed with the Standard Version of the Package, in its current version or as it may be modified by The Perl Foundation in the future. "Source" form means the source code, documentation source, and configuration files for the Package. "Compiled" form means the compiled bytecode, object code, binary, or any other form resulting from mechanical transformation or translation of the Source form. Permission for Use and Modification Without Distribution (1) You are permitted to use the Standard Version and create and use Modified Versions for any purpose without restriction, provided that you do not Distribute the Modified Version. Permissions for Redistribution of the Standard Version (2) You may Distribute verbatim copies of the Source form of the Standard Version of this Package in any medium without restriction, either gratis or for a Distributor Fee, provided that you duplicate all of the original copyright notices and associated disclaimers. At your discretion, such verbatim copies may or may not include a Compiled form of the Package. (3) You may apply any bug fixes, portability changes, and other modifications made available from the Copyright Holder. The resulting Package will still be considered the Standard Version, and as such will be subject to the Original License. Distribution of Modified Versions of the Package as Source (4) You may Distribute your Modified Version as Source (either gratis or for a Distributor Fee, and with or without a Compiled form of the Modified Version) provided that you clearly document how it differs from the Standard Version, including, but not limited to, documenting any non-standard features, executables, or modules, and provided that you do at least ONE of the following: (a) make the Modified Version available to the Copyright Holder of the Standard Version, under the Original License, so that the Copyright Holder may include your modifications in the Standard Version. (b) ensure that installation of your Modified Version does not prevent the user installing or running the Standard Version. In addition, the Modified Version must bear a name that is different from the name of the Standard Version. (c) allow anyone who receives a copy of the Modified Version to make the Source form of the Modified Version available to others under (i) the Original License or (ii) a license that permits the licensee to freely copy, modify and redistribute the Modified Version using the same licensing terms that apply to the copy that the licensee received, and requires that the Source form of the Modified Version, and of any works derived from it, be made freely available in that license fees are prohibited but Distributor Fees are allowed. Distribution of Compiled Forms of the Standard Version or Modified Versions without the Source (5) You may Distribute Compiled forms of the Standard Version without the Source, provided that you include complete instructions on how to get the Source of the Standard Version. Such instructions must be valid at the time of your distribution. If these instructions, at any time while you are carrying out such distribution, become invalid, you must provide new instructions on demand or cease further distribution. If you provide valid instructions or cease distribution within thirty days after you become aware that the instructions are invalid, then you do not forfeit any of your rights under this license. (6) You may Distribute a Modified Version in Compiled form without the Source, provided that you comply with Section 4 with respect to the Source of the Modified Version. Aggregating or Linking the Package (7) You may aggregate the Package (either the Standard Version or Modified Version) with other packages and Distribute the resulting aggregation provided that you do not charge a licensing fee for the Package. Distributor Fees are permitted, and licensing fees for other components in the aggregation are permitted. The terms of this license apply to the use and Distribution of the Standard or Modified Versions as included in the aggregation. (8) You are permitted to link Modified and Standard Versions with other works, to embed the Package in a larger work of your own, or to build stand-alone binary or bytecode versions of applications that include the Package, and Distribute the result without restriction, provided the result does not expose a direct interface to the Package. Items That are Not Considered Part of a Modified Version (9) Works (including, but not limited to, modules and scripts) that merely extend or make use of the Package, do not, by themselves, cause the Package to be a Modified Version. In addition, such works are not considered parts of the Package itself, and are not subject to the terms of this license. General Provisions (10) Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license. (11) If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license. (12) This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder. (13) This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed. (14) Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. INSTALL100644001750001750 462314150724762 14717 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008This is the Perl distribution Mojo-SQLite. Installing Mojo-SQLite is straightforward. ## Installation with cpanm If you have cpanm, you only need one line: % cpanm Mojo::SQLite If it does not have permission to install modules to the current perl, cpanm will automatically set up and install to a local::lib in your home directory. See the local::lib documentation (https://metacpan.org/pod/local::lib) for details on enabling it in your environment. ## Installing with the CPAN shell Alternatively, if your CPAN shell is set up, you should just be able to do: % cpan Mojo::SQLite ## Manual installation As a last resort, you can manually install it. If you have not already downloaded the release tarball, you can find the download link on the module's MetaCPAN page: https://metacpan.org/pod/Mojo::SQLite Untar the tarball, install configure prerequisites (see below), then build it: % perl Build.PL % ./Build && ./Build test Then install it: % ./Build install Or the more portable variation: % perl Build.PL % perl Build % perl Build test % perl Build install If your perl is system-managed, you can create a local::lib in your home directory to install modules to. For details, see the local::lib documentation: https://metacpan.org/pod/local::lib The prerequisites of this distribution will also have to be installed manually. The prerequisites are listed in one of the files: `MYMETA.yml` or `MYMETA.json` generated by running the manual build process described above. ## Configure Prerequisites This distribution requires other modules to be installed before this distribution's installer can be run. They can be found under the "configure_requires" key of META.yml or the "{prereqs}{configure}{requires}" key of META.json. ## Other Prerequisites This distribution may require additional modules to be installed after running Build.PL. Look for prerequisites in the following phases: * to run ./Build, PHASE = build * to use the module code itself, PHASE = runtime * to run tests, PHASE = test They can all be found in the "PHASE_requires" key of MYMETA.yml or the "{prereqs}{PHASE}{requires}" key of MYMETA.json. ## Documentation Mojo-SQLite documentation is available as POD. You can run `perldoc` from a shell to read the documentation: % perldoc Mojo::SQLite For more information on installing Perl modules via CPAN, please see: https://www.cpan.org/modules/INSTALL.html dist.ini100644001750001750 45414150724762 15310 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008name = Mojo-SQLite author = Dan Book license = Artistic_2_0 copyright_holder = Dan Book copyright_year = 2015 [@Author::DBOOK] :version = v1.0.3 installer = ModuleBuildTiny::Fallback irc = ircs://irc.libera.chat/#mojo pod_tests = 1 [Deprecated] module = Mojo::SQLite::PubSub t000755001750001750 014150724762 13764 5ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008crud.t100644001750001750 623014150724762 15247 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/tuse Mojo::Base -strict; BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } use Test::More; use Mojo::SQLite; use Mojo::IOLoop; my $sql = Mojo::SQLite->new; { my $db = $sql->db; $db->query( 'create table if not exists crud_test ( id integer primary key autoincrement, name text )' ); subtest 'Create' => sub { $db->insert('crud_test', {name => 'foo'}); is_deeply $db->select('crud_test')->hashes->to_array, [{id => 1, name => 'foo'}], 'right structure'; is $db->insert('crud_test', {name => 'bar'})->last_insert_id, 2, 'right value'; is_deeply $db->select('crud_test')->hashes->to_array, [{id => 1, name => 'foo'}, {id => 2, name => 'bar'}], 'right structure'; }; subtest 'Read' => sub { is_deeply $db->select('crud_test')->hashes->to_array, [{id => 1, name => 'foo'}, {id => 2, name => 'bar'}], 'right structure'; is_deeply $db->select('crud_test', ['name'])->hashes->to_array, [{name => 'foo'}, {name => 'bar'}], 'right structure'; is_deeply $db->select('crud_test', ['name'], {name => 'foo'})->hashes->to_array, [{name => 'foo'}], 'right structure'; is_deeply $db->select('crud_test', ['name'], undef, {-desc => 'id'}) ->hashes->to_array, [{name => 'bar'}, {name => 'foo'}], 'right structure'; }; subtest 'Non-blocking read' => sub { my $result; $db->select_p('crud_test')->then(sub { $result = shift->hashes->to_array; })->wait; is_deeply $result, [{id => 1, name => 'foo'}, {id => 2, name => 'bar'}], 'right structure'; $result = undef; $db->select_p('crud_test', undef, undef, {-desc => 'id'})->then(sub { $result = shift->hashes->to_array; })->wait; is_deeply $result, [{id => 2, name => 'bar'}, {id => 1, name => 'foo'}], 'right structure'; }; subtest 'Update' => sub { $db->update('crud_test', {name => 'baz'}, {name => 'foo'}); is_deeply $db->select('crud_test', undef, undef, {-asc => 'id'}) ->hashes->to_array, [{id => 1, name => 'baz'}, {id => 2, name => 'bar'}], 'right structure'; }; subtest 'Delete' => sub { $db->delete('crud_test', {name => 'baz'}); is_deeply $db->select('crud_test', undef, undef, {-asc => 'id'}) ->hashes->to_array, [{id => 2, name => 'bar'}], 'right structure'; $db->delete('crud_test'); is_deeply $db->select('crud_test')->hashes->to_array, [], 'right structure'; }; subtest 'Quoting' => sub { $db->query( 'create table if not exists crud_test2 ( id integer primary key autoincrement, "t e s t" text )' ); $db->insert('crud_test2', {'t e s t' => 'foo'}); $db->insert('main.crud_test2', {'t e s t' => 'bar'}); is_deeply $db->select('main.crud_test2')->hashes->to_array, [{id => 1, 't e s t' => 'foo'}, {id => 2, 't e s t' => 'bar'}], 'right structure'; }; subtest 'Unresolved identifier' => sub { is_deeply $db->select('main.crud_test2', undef, {'t e s t' => 'foo'}) ->hashes->to_array, [{id => 1, 't e s t' => 'foo'}], 'right structure'; ok !eval { $db->select('main.crud_test2', undef, {'test' => 'foo'}); 1 }, 'unknown column'; }; } done_testing(); META.yml100644001750001750 343114150724762 15133 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008--- abstract: 'A tiny Mojolicious wrapper for SQLite' author: - 'Dan Book ' build_requires: File::Spec: '0' Module::Metadata: '0' Test::More: '0.96' configure_requires: Module::Build::Tiny: '0.034' dynamic_config: 0 generated_by: 'Dist::Zilla version 6.022, CPAN::Meta::Converter version 2.150010' license: artistic_2 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Mojo-SQLite no_index: directory: - eg - examples - inc - share - t - xt provides: Mojo::SQLite: file: lib/Mojo/SQLite.pm version: '3.008' Mojo::SQLite::Database: file: lib/Mojo/SQLite/Database.pm version: '3.008' Mojo::SQLite::Migrations: file: lib/Mojo/SQLite/Migrations.pm version: '3.008' Mojo::SQLite::PubSub: file: lib/Mojo/SQLite/PubSub.pm version: '3.008' x_deprecated: 1 Mojo::SQLite::Results: file: lib/Mojo/SQLite/Results.pm version: '3.008' Mojo::SQLite::Transaction: file: lib/Mojo/SQLite/Transaction.pm version: '3.008' requires: Carp: '0' DBD::SQLite: '1.68' DBI: '1.627' File::Spec::Functions: '0' File::Temp: '0' Mojolicious: '8.03' SQL::Abstract::Pg: '1.0' Scalar::Util: '0' URI: '1.69' URI::db: '0.15' URI::file: '4.21' perl: '5.010001' resources: IRC: ircs://irc.libera.chat/#mojo bugtracker: https://github.com/Grinnz/Mojo-SQLite/issues homepage: https://github.com/Grinnz/Mojo-SQLite repository: https://github.com/Grinnz/Mojo-SQLite.git version: '3.008' x_contributors: - 'Adam Williamson ' - 'Dan Book ' - 'Dan Book ' - 'Stefan Adams ' x_generated_by_perl: v5.34.0 x_serialization_backend: 'YAML::Tiny version 1.73' x_spdx_expression: Artistic-2.0 MANIFEST100644001750001750 200014150724762 15002 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.022. Build.PL CONTRIBUTING.md Changes INSTALL LICENSE MANIFEST META.json META.yml README dist.ini examples/blog/blog.conf examples/blog/lib/Blog.pm examples/blog/lib/Blog/Controller/Posts.pm examples/blog/lib/Blog/Model/Posts.pm examples/blog/migrations/blog.sql examples/blog/script/blog examples/blog/t/blog.t examples/blog/templates/layouts/blog.html.ep examples/blog/templates/posts/_form.html.ep examples/blog/templates/posts/create.html.ep examples/blog/templates/posts/edit.html.ep examples/blog/templates/posts/index.html.ep examples/blog/templates/posts/show.html.ep lib/Mojo/SQLite.pm lib/Mojo/SQLite/Database.pm lib/Mojo/SQLite/Migrations.pm lib/Mojo/SQLite/PubSub.pm lib/Mojo/SQLite/Results.pm lib/Mojo/SQLite/Transaction.pm prereqs.yml t/00-report-prereqs.dd t/00-report-prereqs.t t/connection.t t/crud.t t/database.t t/migrations.t t/migrations/test.sql t/results.t t/sqlite_lite_app.t xt/author/pod-coverage.t xt/author/pod-syntax.t Build.PL100644001750001750 707214150724762 15163 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008# This Build.PL for Mojo-SQLite was generated by # Dist::Zilla::Plugin::ModuleBuildTiny::Fallback 0.025 use strict; use warnings; my %configure_requires = ( 'Module::Build::Tiny' => '0.034', ); my %errors = map { eval "require $_; $_->VERSION($configure_requires{$_}); 1"; $_ => $@, } keys %configure_requires; if (!grep { $_ } values %errors) { # This section for Mojo-SQLite was generated by Dist::Zilla::Plugin::ModuleBuildTiny 0.015. use strict; use warnings; use 5.010001; # use Module::Build::Tiny 0.034; Module::Build::Tiny::Build_PL(); } else { if (not $ENV{PERL_MB_FALLBACK_SILENCE_WARNING}) { warn <<'EOW' *** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING *** If you're seeing this warning, your toolchain is really, really old* and you'll almost certainly have problems installing CPAN modules from this century. But never fear, dear user, for we have the technology to fix this! If you're using CPAN.pm to install things, then you can upgrade it using: cpan CPAN If you're using CPANPLUS to install things, then you can upgrade it using: cpanp CPANPLUS If you're using cpanminus, you shouldn't be seeing this message in the first place, so please file an issue on github. This public service announcement was brought to you by the Perl Toolchain Gang, the irc.perl.org #toolchain IRC channel, and the number 42. ---- * Alternatively, you are running this file manually, in which case you need to learn to first fulfill all configure requires prerequisites listed in META.yml or META.json -- or use a cpan client to install this distribution. You can also silence this warning for future installations by setting the PERL_MB_FALLBACK_SILENCE_WARNING environment variable, but please don't do that until you fix your toolchain as described above. Errors from configure prereqs: EOW . do { require Data::Dumper; Data::Dumper->new([ \%errors ])->Indent(2)->Terse(1)->Sortkeys(1)->Dump; }; sleep 10 if -t STDIN && (-t STDOUT || !(-f STDOUT || -c STDOUT)); } # This section was automatically generated by Dist::Zilla::Plugin::ModuleBuild v6.022. use strict; use warnings; require Module::Build; Module::Build->VERSION(0.28); my %module_build_args = ( "configure_requires" => { "Module::Build::Tiny" => "0.034" }, "dist_abstract" => "A tiny Mojolicious wrapper for SQLite", "dist_author" => [ "Dan Book " ], "dist_name" => "Mojo-SQLite", "dist_version" => "3.008", "license" => "artistic_2", "module_name" => "Mojo::SQLite", "recursive_test_files" => 1, "requires" => { "Carp" => 0, "DBD::SQLite" => "1.68", "DBI" => "1.627", "File::Spec::Functions" => 0, "File::Temp" => 0, "Mojolicious" => "8.03", "SQL::Abstract::Pg" => "1.0", "Scalar::Util" => 0, "URI" => "1.69", "URI::db" => "0.15", "URI::file" => "4.21", "perl" => "5.010001" }, "test_requires" => { "File::Spec" => 0, "Module::Metadata" => 0, "Test::More" => "0.96" } ); my %fallback_build_requires = ( "File::Spec" => 0, "Module::Metadata" => 0, "Test::More" => "0.96" ); unless ( eval { Module::Build->VERSION(0.4004) } ) { delete $module_build_args{test_requires}; $module_build_args{build_requires} = \%fallback_build_requires; } my $build = Module::Build->new(%module_build_args); $build->create_build_script; } META.json100644001750001750 611714150724762 15307 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008{ "abstract" : "A tiny Mojolicious wrapper for SQLite", "author" : [ "Dan Book " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.022, CPAN::Meta::Converter version 2.150010", "license" : [ "artistic_2" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Mojo-SQLite", "no_index" : { "directory" : [ "eg", "examples", "inc", "share", "t", "xt" ] }, "prereqs" : { "configure" : { "requires" : { "Module::Build::Tiny" : "0.034" } }, "develop" : { "requires" : { "Pod::Coverage::TrustPod" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08" } }, "runtime" : { "requires" : { "Carp" : "0", "DBD::SQLite" : "1.68", "DBI" : "1.627", "File::Spec::Functions" : "0", "File::Temp" : "0", "Mojolicious" : "8.03", "SQL::Abstract::Pg" : "1.0", "Scalar::Util" : "0", "URI" : "1.69", "URI::db" : "0.15", "URI::file" : "4.21", "perl" : "5.010001" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "File::Spec" : "0", "Module::Metadata" : "0", "Test::More" : "0.96" } } }, "provides" : { "Mojo::SQLite" : { "file" : "lib/Mojo/SQLite.pm", "version" : "3.008" }, "Mojo::SQLite::Database" : { "file" : "lib/Mojo/SQLite/Database.pm", "version" : "3.008" }, "Mojo::SQLite::Migrations" : { "file" : "lib/Mojo/SQLite/Migrations.pm", "version" : "3.008" }, "Mojo::SQLite::PubSub" : { "file" : "lib/Mojo/SQLite/PubSub.pm", "version" : "3.008", "x_deprecated" : 1 }, "Mojo::SQLite::Results" : { "file" : "lib/Mojo/SQLite/Results.pm", "version" : "3.008" }, "Mojo::SQLite::Transaction" : { "file" : "lib/Mojo/SQLite/Transaction.pm", "version" : "3.008" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/Grinnz/Mojo-SQLite/issues" }, "homepage" : "https://github.com/Grinnz/Mojo-SQLite", "repository" : { "type" : "git", "url" : "https://github.com/Grinnz/Mojo-SQLite.git", "web" : "https://github.com/Grinnz/Mojo-SQLite" }, "x_IRC" : "ircs://irc.libera.chat/#mojo" }, "version" : "3.008", "x_contributors" : [ "Adam Williamson ", "Dan Book ", "Dan Book ", "Stefan Adams " ], "x_generated_by_perl" : "v5.34.0", "x_serialization_backend" : "Cpanel::JSON::XS version 4.26", "x_spdx_expression" : "Artistic-2.0" } prereqs.yml100644001750001750 54514150724762 16051 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008runtime: requires: perl: '5.010001' Carp: 0 DBI: '1.627' DBD::SQLite: '1.68' # for JSON1, FTS5, DQS, sqlite_string_mode File::Spec::Functions: 0 File::Temp: 0 Mojolicious: '8.03' Scalar::Util: 0 SQL::Abstract::Pg: '1.0' URI: '1.69' URI::db: '0.15' URI::file: '4.21' test: requires: Test::More: '0.96' results.t100644001750001750 1346114150724762 16037 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/tuse Mojo::Base -strict; use Test::More; use Mojo::SQLite; use Mojo::IOLoop; use Mojo::Promise; package MojoSQLiteTest::Database; use Mojo::Base 'Mojo::SQLite::Database'; sub results_class {'MojoSQLiteTest::Results'} package MojoSQLiteTest::Results; use Mojo::Base 'Mojo::SQLite::Results'; sub array_test { shift->array } package main; my $sql = Mojo::SQLite->new; { my $db = $sql->db; $db->query( 'create table if not exists results_test ( id integer primary key autoincrement, name text )' ); my $results; $results = $db->query('insert into results_test (name) values (?)', $_) for qw(foo bar); is $results->last_insert_id, 2, 'right last_insert_id'; is $db->query('update results_test set name=name')->rows, 2, 'two rows affected'; subtest 'Tables' => sub { ok !!(grep {/^"main"."results_test"$/i} @{$db->tables}), 'results table exists'; ok !(grep {/^"main"."sqlite_master"$/i} @{$db->tables}), 'internal tables are hidden'; ok !(grep {/^"temp"."sqlite_temp_master"$/i} @{$db->tables}), 'internal tables are hidden'; }; subtest 'Result methods' => sub { is_deeply $db->query('select * from results_test')->columns, ['id', 'name'], 'right structure'; is_deeply $db->query('select * from results_test')->array, [1, 'foo'], 'right structure'; my $results = $db->query('select * from results_test'); is_deeply $results->arrays->to_array, [[1, 'foo'], [2, 'bar']], 'right structure'; is_deeply $results->rows, 2, 'two rows'; is_deeply $db->query('select * from results_test')->hash, {id => 1, name => 'foo'}, 'right structure'; is_deeply $db->query('select * from results_test')->hashes->to_array, [{id => 1, name => 'foo'}, {id => 2, name => 'bar'}], 'right structure'; is $sql->db->query('select * from results_test')->text, "1 foo\n2 bar\n", 'right text'; }; subtest 'Custom database and results classes' => sub { is ref $db, 'Mojo::SQLite::Database', 'right class'; $sql->database_class('MojoSQLiteTest::Database'); $db = $sql->db; is ref $db, 'MojoSQLiteTest::Database', 'right class'; is ref $db->query('select 1'), 'MojoSQLiteTest::Results', 'right class'; is_deeply $db->query('select * from results_test')->array_test, [1, 'foo'], 'right structure'; }; subtest 'JSON' => sub { is_deeply $db->query('select ? as foo', {json => {bar => 'baz'}}) ->expand(json => 'foo')->hash, {foo => {bar => 'baz'}}, 'right structure'; is_deeply $db->query('select ? as foo', {-json => {bar => 'baz'}}) ->expand(json => 'foo')->hash, {foo => {bar => 'baz'}}, 'right structure'; is_deeply $db->query('select ? as foo', {json => {bar => 'baz'}}) ->expand(json => 'foo')->array, [{bar => 'baz'}], 'right structure'; my $hashes = [{foo => {one => 1}, bar => 'a'}, {foo => {two => 2}, bar => 'b'}]; is_deeply $db->query( "select 'a' as bar, ? as foo union all select 'b' as bar, ? as foo", {json => {one => 1}}, {json => {two => 2}} )->expand(json => 'foo')->hashes->to_array, $hashes, 'right structure'; my $arrays = [['a', {one => 1}], ['b', {two => 2}]]; is_deeply $db->query( "select 'a' as bar, ? as foo union all select 'b' as bar, ? as foo", {json => {one => 1}}, {json => {two => 2}} )->expand(json => 'foo')->arrays->to_array, $arrays, 'right structure'; }; subtest 'Iterate' => sub { my $results = $db->query('select * from results_test'); is_deeply $results->array, [1, 'foo'], 'right structure'; is_deeply $results->array, [2, 'bar'], 'right structure'; is $results->array, undef, 'no more results'; }; subtest 'Non-blocking query where not all results have been fetched' => sub { my ($fail, $result); my @promises = ( $db->query_p('select name from results_test')->then(sub { my $results = shift; push @$result, $results->array; $results->finish; }), $db->query_p('select name from results_test')->then(sub { my $results = shift; push @$result, $results->array_test; $results->finish; }), $db->query_p('select name from results_test')->then(sub { my $results = shift; push @$result, $results->array; }), ); Mojo::Promise->all(@promises)->catch(sub { $fail = 1 })->wait; ok !$fail, 'no error'; is_deeply $result, [['foo'], ['foo'], ['foo']], 'right structure'; }; subtest 'Transactions' => sub { { my $tx = $db->begin; $db->query("insert into results_test (name) values ('tx1')"); $db->query("insert into results_test (name) values ('tx1')"); $tx->commit; } is_deeply $db->query('select * from results_test where name = ?', 'tx1') ->hashes->to_array, [{id => 3, name => 'tx1'}, {id => 4, name => 'tx1'}], 'right structure'; { my $tx = $db->begin; $db->query("insert into results_test (name) values ('tx2')"); $db->query("insert into results_test (name) values ('tx2')"); } is_deeply $db->query('select * from results_test where name = ?', 'tx2') ->hashes->to_array, [], 'no results'; eval { my $tx = $db->begin; $db->query("insert into results_test (name) values ('tx3')"); $db->query("insert into results_test (name) values ('tx3')"); $db->query('does_not_exist'); $tx->commit; }; like $@, qr/does_not_exist/, 'right error'; is_deeply $db->query('select * from results_test where name = ?', 'tx3') ->hashes->to_array, [], 'no results'; }; subtest 'Issue #2' => sub { my $results1 = $db->query('select 1 as one'); is_deeply $results1->hashes, [{one => 1}], 'right structure'; my $results2 = $db->query('select 1 as one'); undef $results1; is_deeply $results2->hashes, [{one => 1}], 'right structure'; } } done_testing(); database.t100644001750001750 2235514150724762 16104 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/tuse Mojo::Base -strict; BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } use Test::More; use Mojo::SQLite; use Mojo::IOLoop; use Mojo::JSON 'true'; use DBI ':sql_types'; use Mojo::Util 'encode'; my $sql = Mojo::SQLite->new; subtest 'Connected' => sub { ok $sql->db->ping, 'connected'; }; subtest 'Blocking select' => sub { is_deeply $sql->db->query('select 1 as one, 2 as two, 3 as three')->hash, {one => 1, two => 2, three => 3}, 'right structure'; }; subtest 'Non-blocking select' => sub { my ($fail, $result); my $same; my $db = $sql->db; $db->query( 'select 1 as one, 2 as two, 3 as three' => sub { my ($db, $err, $results) = @_; $fail = $err; $result = $results->hash; $same = $db->dbh eq $results->db->dbh; Mojo::IOLoop->stop; } ); Mojo::IOLoop->start; ok $same, 'same database handles'; ok !$fail, 'no error'; is_deeply $result, {one => 1, two => 2, three => 3}, 'right structure'; }; subtest 'Concurrent non-blocking selects' => sub { my ($fail, $result); my $one = $sql->db->query_p('select 1 as one'); my $two = $sql->db->query_p('select 2 as two'); my $again = $sql->db->query_p('select 2 as two'); Mojo::Promise->all($one, $again, $two)->then(sub { $result = [map { $_->[0]->hashes->first } @_]; })->catch(sub { $fail = 1; })->wait; ok !$fail, 'no error'; is_deeply $result, [{one => 1}, {two => 2}, {two => 2}], 'right structure'; }; subtest 'Sequential non-blocking selects' => sub { my ($fail, $result); my $db = $sql->db; $db->query_p('select 1 as one')->then(sub { push @$result, shift->hashes->first; $db->query_p('select 1 as one'); })->then(sub { push @$result, shift->hashes->first; $db->query_p('select 2 as two'); })->then(sub { push @$result, shift->hashes->first; })->catch(sub { $fail = 1; })->wait; ok !$fail, 'no error'; is_deeply $result, [{one => 1}, {one => 1}, {two => 2}], 'right structure'; }; subtest 'Connection cache' => sub { is $sql->max_connections, 1, 'right default'; my @dbhs = map { $_->dbh } $sql->db, $sql->db, $sql->db, $sql->db, $sql->db; is_deeply \@dbhs, [map { $_->dbh } $sql->db, $sql->db, $sql->db, $sql->db, $sql->db], 'same database handles'; @dbhs = (); my $dbh = $sql->max_connections(1)->db->dbh; is $sql->db->dbh, $dbh, 'same database handle'; isnt $sql->db->dbh, $sql->db->dbh, 'different database handles'; is $sql->db->dbh, $dbh, 'different database handles'; $dbh = $sql->db->dbh; is $sql->db->dbh, $dbh, 'same database handle'; $sql->db->disconnect; isnt $sql->db->dbh, $dbh, 'different database handles'; }; subtest 'Statement cache' => sub { my $db = $sql->db; my $sth = $db->query('select 3 as three')->sth; is $db->query('select 3 as three')->sth, $sth, 'same statement handle'; isnt $db->query('select 4 as four')->sth, $sth, 'different statement handles'; is $db->query('select 3 as three')->sth, $sth, 'same statement handle'; undef $db; $db = $sql->db; my $results = $db->query('select 3 as three'); is $results->sth, $sth, 'same statement handle'; isnt $db->query('select 3 as three')->sth, $sth, 'different statement handles'; $sth = $db->query('select 3 as three')->sth; is $db->query('select 3 as three')->sth, $sth, 'same statement handle'; isnt $db->query('select 5 as five')->sth, $sth, 'different statement handles'; isnt $db->query('select 6 as six')->sth, $sth, 'different statement handles'; is $db->query('select 3 as three')->sth, $sth, 'same statement handle'; }; subtest 'Connection reuse' => sub { my $db = $sql->db; my $dbh = $db->dbh; my $results = $db->query('select 1'); undef $db; my $db2 = $sql->db; isnt $db2->dbh, $dbh, 'new database handle'; undef $results; my $db3 = $sql->db; is $db3->dbh, $dbh, 'same database handle'; $results = $db3->query('select 2'); is $results->db->dbh, $dbh, 'same database handle'; is $results->array->[0], 2, 'right result'; }; subtest 'Bind types' => sub { my $db = $sql->db; is_deeply $db->query('select ? as foo', {type => SQL_VARCHAR, value => 'bar'}) ->hash, {foo => 'bar'}, 'right structure'; is_deeply $db->query('select ? as foo', {type => SQL_INTEGER, value => 5}) ->hash, {foo => 5}, 'right structure'; is_deeply $db->query('select ? as foo', {type => SQL_REAL, value => 2.5}) ->hash, {foo => 2.5}, 'right structure'; is_deeply $db->query('select ? as foo', {type => SQL_VARCHAR, value => '☃♥'}) ->hash, {foo => '☃♥'}, 'right structure'; is_deeply $db->query('select ? as foo', {type => SQL_BLOB, value => encode 'UTF-8', '☃♥'}) ->hash, {foo => encode 'UTF-8', '☃♥'}, 'right structure'; }; subtest 'JSON' => sub { my $db = $sql->db; is_deeply $db->query('select ? as foo', {json => {bar => 'baz'}}) ->expand(json => 'foo')->hash, {foo => {bar => 'baz'}}, 'right structure'; is_deeply $db->query('select ? as foo', {json => {bar => 'baz'}}) ->expand(json => 'foo')->array, [{bar => 'baz'}], 'right structure'; is_deeply $db->query('select ? as foo', {json => {bar => 'baz'}}) ->expand(json => 'foo')->hashes->first, {foo => {bar => 'baz'}}, 'right structure'; is_deeply $db->query('select ? as foo', {json => {bar => 'baz'}}) ->expand(json => 'foo')->arrays->first, [{bar => 'baz'}], 'right structure'; is_deeply $db->query('select ? as foo', {json => {bar => 'baz'}})->hash, {foo => '{"bar":"baz"}'}, 'right structure'; is_deeply $db->query('select ? as foo', {json => \1}) ->expand(json => 'foo')->hashes->first, {foo => true}, 'right structure'; is_deeply $db->query('select ? as foo', undef)->expand(json => 'foo')->hash, {foo => undef}, 'right structure'; is_deeply $db->query('select ? as foo', undef)->expand(json => 'foo')->array, [undef], 'right structure'; my $results = $db->query('select ?', undef); my $name = $results->columns->[0]; is_deeply $results->expand(json => $name)->array, [undef], 'right structure'; is_deeply $results->expand(json => $name)->array, undef, 'no more results'; is_deeply $db->query('select ? as unicode', {json => {'☃' => '♥'}}) ->expand(json => 'unicode')->hash, {unicode => {'☃' => '♥'}}, 'right structure'; is_deeply $db->query("select json_object('☃', ?) as unicode", '♥') ->expand(json => 'unicode')->hash, {unicode => {'☃' => '♥'}}, 'right structure'; is_deeply $db->query('select ? as foo, ? as bar', {json => {baz => 'foo'}}, {json => {baz => 'bar'}})->expand(json => 'foo')->hash, {foo => {baz => 'foo'}, bar => '{"baz":"bar"}'}, 'right structure'; is_deeply $db->query('select ? as foo, ? as bar', {json => {baz => 'foo'}}, {json => {baz => 'bar'}})->expand(json => ['foo','bar'])->hash, {foo => {baz => 'foo'}, bar => {baz => 'bar'}}, 'right structure'; }; subtest 'Fork-safety' => sub { my $dbh = $sql->db->dbh; my ($connections, $current) = @_; $sql->on( connection => sub { my ($sql, $dbh) = @_; $connections++; $current = $dbh; } ); is $sql->db->dbh, $dbh, 'same database handle'; ok !$connections, 'no new connections'; { local $$ = -23; isnt $sql->db->dbh, $dbh, 'different database handles'; is $sql->db->dbh, $current, 'same database handle'; is $connections, 1, 'one new connection'; } $sql->unsubscribe('connection'); }; subtest 'Shared connection cache' => sub { my $sql2 = Mojo::SQLite->new($sql); is $sql2->parent, $sql, 'right parent'; my $dbh = $sql->db->dbh; is $sql->db->dbh, $dbh, 'same database handle'; is $sql2->db->dbh, $dbh, 'same database handle'; is $sql->db->dbh, $dbh, 'same database handle'; is $sql2->db->dbh, $dbh, 'same database handle'; my $db = $sql->db; is_deeply $db->query('select 1 as one')->hashes->to_array, [{one => 1}], 'right structure'; $dbh = $db->dbh; $db->disconnect; $db = $sql2->db; is_deeply $db->query('select 1 as one')->hashes->to_array, [{one => 1}], 'right structure'; isnt $db->dbh, $dbh, 'different database handle'; }; subtest 'Blocking error' => sub { eval { $sql->db->query('does_not_exist') }; like $@, qr/does_not_exist.*database\.t/s, 'right error'; }; subtest 'Non-blocking error' => sub { my ($fail, $result); my $db = $sql->db; $db->query( 'does_not_exist' => sub { my ($db, $err, $results) = @_; ($fail, $result) = ($err, $results); Mojo::IOLoop->stop; } ); Mojo::IOLoop->start; like $fail, qr/does_not_exist/, 'right error'; is $db->dbh->errstr, $fail, 'same error'; }; subtest 'Error context' => sub { eval { $sql->db->query('select * from table_does_not_exist') }; like $@, qr/database\.t/, 'right error'; }; subtest 'Double-quoted literal' => sub { ok !eval { $sql->db->query('select "does_not_exist"') }, 'no double-quoted string literals'; }; subtest 'WAL mode option' => sub { my $journal_mode = $sql->db->query('pragma journal_mode')->arrays->first->[0]; is uc $journal_mode, 'WAL', 'right journal mode'; my $sql = Mojo::SQLite->new; $sql->options->{wal_mode} = 1; $journal_mode = $sql->db->query('pragma journal_mode')->arrays->first->[0]; is uc $journal_mode, 'WAL', 'right journal mode'; $sql = Mojo::SQLite->new; $sql->options->{no_wal} = 1; $journal_mode = $sql->db->query('pragma journal_mode')->arrays->first->[0]; is uc $journal_mode, 'DELETE', 'right journal mode'; }; done_testing(); connection.t100644001750001750 1021514150724762 16467 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/tuse Mojo::Base -strict; use Test::More; use DBD::SQLite::Constants ':dbd_sqlite_string_mode'; use Mojo::SQLite; use URI::file; subtest 'Defaults' => sub { my $sql = Mojo::SQLite->new; like $sql->dsn, qr/^dbi:SQLite:dbname=/, 'right data source'; my $options = { AutoCommit => 1, AutoInactiveDestroy => 1, PrintError => 0, RaiseError => 1, sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK, wal_mode => 1, }; is_deeply $sql->options, $options, 'right options'; }; subtest 'Minimal connection string with file' => sub { my $sql = Mojo::SQLite->new('test.db'); is $sql->dsn, 'dbi:SQLite:dbname=test.db', 'right data source'; my $options = { AutoCommit => 1, AutoInactiveDestroy => 1, PrintError => 0, RaiseError => 1, sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK, wal_mode => 1, }; is_deeply $sql->options, $options, 'right options'; }; subtest 'Minimal connection string with in-memory database and option' => sub { my $sql = Mojo::SQLite->new('sqlite::memory:?PrintError=1'); is $sql->dsn, 'dbi:SQLite:dbname=:memory:', 'right data source'; my $options = { AutoCommit => 1, AutoInactiveDestroy => 1, PrintError => 1, RaiseError => 1, sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK, wal_mode => 1, }; is_deeply $sql->options, $options, 'right options'; }; subtest 'Connection string with absolute filename and options' => sub { my $uri = URI::file->new('/tmp/sqlite.db?#', 'unix') ->Mojo::Base::tap(query_form => {PrintError => 1, RaiseError => 0}); my $sql; { # Force unix interpretation local %URI::file::OS_CLASS = (); $sql = Mojo::SQLite->new($uri); } is $sql->dsn, 'dbi:SQLite:dbname=/tmp/sqlite.db?#', 'right data source'; my $options = { AutoCommit => 1, AutoInactiveDestroy => 1, PrintError => 1, RaiseError => 0, sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK, wal_mode => 1, }; is_deeply $sql->options, $options, 'right options'; }; subtest 'Connection string with lots of zeros' => sub { my $sql = Mojo::SQLite->new('0?RaiseError=0'); is $sql->dsn, 'dbi:SQLite:dbname=0', 'right data source'; my $options = { AutoCommit => 1, AutoInactiveDestroy => 1, PrintError => 0, RaiseError => 0, sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK, wal_mode => 1, }; is_deeply $sql->options, $options, 'right options'; }; subtest 'Parse filename' => sub { my $sql = Mojo::SQLite->new->from_filename('/foo#/bar?.db', {PrintError => 1}); is $sql->dsn, 'dbi:SQLite:dbname=/foo#/bar?.db', 'right data source'; my $options = { AutoCommit => 1, AutoInactiveDestroy => 1, PrintError => 1, RaiseError => 1, sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK, wal_mode => 1, }; is_deeply $sql->options, $options, 'right options'; }; subtest 'Invalid connection string' => sub { eval { Mojo::SQLite->new('http://localhost:3000/test') }; like $@, qr/Invalid SQLite connection string/, 'right error'; }; subtest 'Legacy sqlite_unicode enabled' => sub { my $sql = Mojo::SQLite->new('test.db?sqlite_unicode=1'); like $sql->dsn, qr/^dbi:SQLite:dbname=/, 'right data source'; my $options = { AutoCommit => 1, AutoInactiveDestroy => 1, PrintError => 0, RaiseError => 1, sqlite_unicode => 1, wal_mode => 1, }; is_deeply $sql->options, $options, 'right options'; }; subtest 'Legacy sqlite_unicode disabled' => sub { my $sql = Mojo::SQLite->new('test.db?sqlite_unicode=0'); like $sql->dsn, qr/^dbi:SQLite:dbname=/, 'right data source'; my $options = { AutoCommit => 1, AutoInactiveDestroy => 1, PrintError => 0, RaiseError => 1, sqlite_unicode => 0, wal_mode => 1, }; is_deeply $sql->options, $options, 'right options'; }; done_testing(); migrations.t100644001750001750 1623714150724762 16516 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/tuse Mojo::Base -strict; use Test::More; use File::Spec::Functions 'catfile'; use File::Temp; use FindBin; use Mojo::SQLite; my $tempdir = File::Temp->newdir; my $tempfile = catfile($tempdir, 'test.db'); # Clean up before start my $sql = Mojo::SQLite->new->from_filename($tempfile); $sql->db->query('drop table if exists mojo_migrations'); subtest 'Defaults' => sub { is $sql->migrations->name, 'migrations', 'right name'; is $sql->migrations->latest, 0, 'latest version is 0'; is $sql->migrations->active, 0, 'active version is 0'; }; subtest 'Create migrations table' => sub { ok !(grep {/^"main"."mojo_migrations"$/i} @{$sql->db->tables}), 'migrations table does not exist'; is $sql->migrations->migrate->active, 0, 'active version is 0'; ok !!(grep {/^"main"."mojo_migrations"$/i} @{$sql->db->tables}), 'migrations table exists'; }; subtest 'Migrations from DATA section' => sub { is $sql->migrations->from_data->latest, 0, 'latest version is 0'; is $sql->migrations->from_data(__PACKAGE__)->latest, 0, 'latest version is 0'; is $sql->migrations->name('test1')->from_data->latest, 10, 'latest version is 10'; is $sql->migrations->name('test2')->from_data->latest, 2, 'latest version is 2'; is $sql->migrations->name('migrations')->from_data(__PACKAGE__, 'test1') ->latest, 10, 'latest version is 10'; is $sql->migrations->name('test2')->from_data(__PACKAGE__)->latest, 2, 'latest version is 2'; }; subtest 'Different syntax variations' => sub { $sql->migrations->name('migrations_test')->from_string(<migrations->latest, 10, 'latest version is 10'; is $sql->migrations->active, 0, 'active version is 0'; is $sql->migrations->migrate->active, 10, 'active version is 10'; ok !!(grep {/^"main"."migration_test_one"$/i} @{$sql->db->tables}), 'first table exists'; ok !!(grep {/^"main"."migration_test_two"$/i} @{$sql->db->tables}), 'second table exists'; is_deeply $sql->db->query('select * from migration_test_one')->hash, {foo => 'works ♥'}, 'right structure'; is $sql->migrations->migrate->active, 10, 'active version is 10'; is $sql->migrations->migrate(1)->active, 1, 'active version is 1'; is $sql->db->query('select * from migration_test_one')->hash, undef, 'no result'; is $sql->migrations->migrate(3)->active, 3, 'active version is 3'; is $sql->db->query('select * from migration_test_two')->hash, undef, 'no result'; is $sql->migrations->migrate->active, 10, 'active version is 10'; is_deeply $sql->db->query('select * from migration_test_two')->hash, {bar => 'works too'}, 'right structure'; is $sql->migrations->migrate(0)->active, 0, 'active version is 0'; }; subtest 'Bad and concurrent migrations' => sub { my $sql2 = Mojo::SQLite->new->from_filename($tempfile); $sql2->migrations->name('migrations_test2') ->from_file(catfile($FindBin::Bin, 'migrations', 'test.sql')); is $sql2->migrations->latest, 4, 'latest version is 4'; is $sql2->migrations->active, 0, 'active version is 0'; eval { $sql2->migrations->migrate }; like $@, qr/does_not_exist/, 'right error'; is $sql2->migrations->migrate(3)->active, 3, 'active version is 3'; is $sql2->migrations->migrate(2)->active, 2, 'active version is 2'; is $sql->migrations->active, 0, 'active version is still 0'; is $sql->migrations->migrate->active, 10, 'active version is 10'; is_deeply $sql2->db->query('select * from migration_test_three') ->hashes->to_array, [{baz => 'just'}, {baz => 'works ♥'}], 'right structure'; is $sql->migrations->migrate(0)->active, 0, 'active version is 0'; is $sql2->migrations->migrate(0)->active, 0, 'active version is 0'; }; subtest 'Migrate automatically' => sub { my $sql3 = Mojo::SQLite->new->from_filename($tempfile); $sql3->migrations->name('migrations_test')->from_string(<<'EOF'); -- 5 up create table if not exists migration_test_six (foo text); -- 6 up insert into migration_test_six values ('works!'); -- 5 down drop table if exists migration_test_six; -- 6 down delete from migration_test_six; EOF $sql3->auto_migrate(1)->db; is $sql3->migrations->active, 6, 'active version is 6'; is_deeply $sql3->db->query('select * from migration_test_six')->hashes, [{foo => 'works!'}], 'right structure'; is $sql3->migrations->migrate(5)->active, 5, 'active version is 5'; is_deeply $sql3->db->query('select * from migration_test_six')->hashes, [], 'right structure'; is $sql3->migrations->migrate(0)->active, 0, 'active version is 0'; is $sql3->migrations->sql_for(0, 5), <migrations->sql_for(6, 0), <migrations->sql_for(6, 5), <migrations->sql_for(6, 6), '', 'right SQL'; is $sql3->migrations->sql_for(2, 3), '', 'right SQL'; }; subtest 'Migrate automatically with shared connection cache' => sub { my $sql4 = Mojo::SQLite->new->from_filename($tempfile); my $sql5 = Mojo::SQLite->new($sql4); $sql4->auto_migrate(1)->migrations->name('test1')->from_data; $sql5->auto_migrate(1)->migrations->name('test3')->from_data; is_deeply $sql5->db->query('select * from migration_test_four') ->hashes->to_array, [{test => 10}], 'right structure'; is_deeply $sql5->db->query('select * from migration_test_six')->hashes->to_array, [], 'right structure'; }; subtest 'Unknown version' => sub { eval { $sql->migrations->migrate(23) }; like $@, qr/Version 23 has no migration/, 'right error'; }; subtest 'Version mismatch' => sub { my $newer = <migrations->name('migrations_test3')->from_string($newer); is $sql->migrations->migrate->active, 2, 'active version is 2'; $sql->migrations->from_string(<migrations->migrate }; like $@, qr/Active version 2 is greater than the latest version 1/, 'right error'; eval { $sql->migrations->migrate(0) }; like $@, qr/Active version 2 is greater than the latest version 1/, 'right error'; is $sql->migrations->from_string($newer)->migrate(0)->active, 0, 'active version is 0'; }; done_testing(); __DATA__ @@ test1 -- 7 up create table migration_test_four (test integer); -- 10 up insert into migration_test_four values (10); @@ test2 -- 2 up create table migration_test_five (test integer); @@ test3 -- 2 up create table migration_test_six (test integer); CONTRIBUTING.md100644001750001750 1055514150724762 16140 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008# HOW TO CONTRIBUTE Thank you for considering contributing to this distribution. This file contains instructions that will help you work with the source code. The distribution is managed with [Dist::Zilla](https://metacpan.org/pod/Dist::Zilla). This means that many of the usual files you might expect are not in the repository, but are generated at release time. Some generated files are kept in the repository as a convenience (e.g. Build.PL/Makefile.PL and META.json). Generally, **you do not need Dist::Zilla to contribute patches**. You may need Dist::Zilla to create a tarball. See below for guidance. ## Getting dependencies If you have App::cpanminus 1.6 or later installed, you can use [cpanm](https://metacpan.org/pod/cpanm) to satisfy dependencies like this: $ cpanm --installdeps --with-develop . You can also run this command (or any other cpanm command) without installing App::cpanminus first, using the fatpacked `cpanm` script via curl or wget: $ curl -L https://cpanmin.us | perl - --installdeps --with-develop . $ wget -qO - https://cpanmin.us | perl - --installdeps --with-develop . Otherwise, look for either a `cpanfile`, `prereqs.json`/`prereqs.yml`, or `META.json` file for a list of dependencies to satisfy. ## Running tests You can run tests directly using the `prove` tool: $ prove -l $ prove -lv t/some_test_file.t For most of my distributions, `prove` is entirely sufficient for you to test any patches you have. I use `prove` for 99% of my testing during development. ## Code style and tidying Please try to match any existing coding style. If there is a `.perltidyrc` file, please install Perl::Tidy and use perltidy before submitting patches. ## Installing and using Dist::Zilla [Dist::Zilla](https://metacpan.org/pod/Dist::Zilla) is a very powerful authoring tool, optimized for maintaining a large number of distributions with a high degree of automation, but it has a large dependency chain, a bit of a learning curve and requires a number of author-specific plugins. To install it from CPAN, I recommend one of the following approaches for the quickest installation: # using CPAN.pm, but bypassing non-functional pod tests $ cpan TAP::Harness::Restricted $ PERL_MM_USE_DEFAULT=1 HARNESS_CLASS=TAP::Harness::Restricted cpan Dist::Zilla # using cpanm, bypassing *all* tests $ cpanm -n Dist::Zilla In either case, it's probably going to take about 10 minutes. Go for a walk, go get a cup of your favorite beverage, take a bathroom break, or whatever. When you get back, Dist::Zilla should be ready for you. Then you need to install any plugins specific to this distribution: $ dzil authordeps --missing | cpanm You can use Dist::Zilla to install the distribution's dependencies if you haven't already installed them with cpanm: $ dzil listdeps --missing --develop | cpanm You can instead combine these two steps into one command by installing Dist::Zilla::App::Command::installdeps then running: $ dzil installdeps Once everything is installed, here are some dzil commands you might try: $ dzil build $ dzil test $ dzil regenerate You can learn more about Dist::Zilla at http://dzil.org/ ## Other notes This distribution maintains the generated `META.json` and either `Makefile.PL` or `Build.PL` in the repository. This allows two things: [Travis CI](https://travis-ci.org/) can build and test the distribution without requiring Dist::Zilla, and the distribution can be installed directly from Github or a local git repository using `cpanm` for testing (again, not requiring Dist::Zilla). $ cpanm git://github.com/Author/Distribution-Name.git $ cd Distribution-Name; cpanm . Contributions are preferred in the form of a Github pull request. See [Using pull requests](https://help.github.com/articles/using-pull-requests/) for further information. You can use the Github issue tracker to report issues without an accompanying patch. # CREDITS This file was adapted from an initial `CONTRIBUTING.mkdn` file from David Golden under the terms of the [CC0](https://creativecommons.org/share-your-work/public-domain/cc0/), with inspiration from the contributing documents from [Dist::Zilla::Plugin::Author::KENTNL::CONTRIBUTING](https://metacpan.org/pod/Dist::Zilla::Plugin::Author::KENTNL::CONTRIBUTING) and [Dist::Zilla::PluginBundle::Author::ETHER](https://metacpan.org/pod/Dist::Zilla::PluginBundle::Author::ETHER). Mojo000755001750001750 014150724762 15173 5ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/libSQLite.pm100644001750001750 4516714150724762 17067 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/lib/Mojopackage Mojo::SQLite; use Mojo::Base 'Mojo::EventEmitter'; use Carp 'croak'; use DBI; use DBD::SQLite; use DBD::SQLite::Constants qw(:database_connection_configuration_options :dbd_sqlite_string_mode); use File::Spec::Functions 'catfile'; use File::Temp; use Mojo::SQLite::Database; use Mojo::SQLite::Migrations; use Scalar::Util qw(blessed weaken); use SQL::Abstract::Pg; use URI; use URI::db; our $VERSION = '3.008'; has abstract => sub { SQL::Abstract::Pg->new(name_sep => '.', quote_char => '"') }; has 'auto_migrate'; has database_class => 'Mojo::SQLite::Database'; has dsn => sub { _url_from_file(shift->_tempfile)->dbi_dsn }; has max_connections => 1; has migrations => sub { Mojo::SQLite::Migrations->new(sqlite => shift) }; has options => sub { { AutoCommit => 1, AutoInactiveDestroy => 1, PrintError => 0, RaiseError => 1, sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK, wal_mode => 1, }; }; has 'parent'; sub new { @_ > 1 ? shift->SUPER::new->from_string(@_) : shift->SUPER::new } sub db { $_[0]->database_class->new(dbh => $_[0]->_prepare, sqlite => $_[0]) } sub from_filename { shift->from_string(_url_from_file(shift, shift)) } sub from_string { my ($self, $str) = @_; return $self unless $str; return $self->parent($str) if blessed $str and $str->isa('Mojo::SQLite'); my $url = URI->new($str); # Options my %options = $url->query_form; $url->query(undef); # don't set default string_mode if sqlite_unicode legacy option is set delete $self->options->{sqlite_string_mode} if exists $options{sqlite_unicode}; @{$self->options}{keys %options} = values %options; # Parse URL based on scheme $url->scheme('file') unless $url->has_recognized_scheme; if ($url->scheme eq 'file') { $url = _url_from_file($url->file); } elsif ($url->scheme ne 'db') { $url = URI::db->new($url); } croak qq{Invalid SQLite connection string "$str"} unless $url->has_recognized_engine and $url->canonical_engine eq 'sqlite' and (($url->host // '') eq '' or $url->host eq 'localhost'); # Temp database file $url->dbname($self->_tempfile) if $url->dbname eq ':temp:'; return $self->dsn($url->dbi_dsn); } sub _dequeue { my $self = shift; # Fork-safety delete @$self{qw(pid queue)} unless ($self->{pid} //= $$) eq $$; while (my $dbh = shift @{$self->{queue} || []}) { return $dbh if $dbh->ping } my $dbh = DBI->connect($self->dsn, undef, undef, $self->options) // croak "DBI connection to @{[$self->dsn]} failed: $DBI::errstr"; # RaiseError disabled $dbh->sqlite_db_config(SQLITE_DBCONFIG_DQS_DDL, 0); $dbh->sqlite_db_config(SQLITE_DBCONFIG_DQS_DML, 0); if ($self->options->{wal_mode} and !$self->options->{no_wal}) { $dbh->do('pragma journal_mode=WAL'); $dbh->do('pragma synchronous=NORMAL'); } # Cache the last insert rowid on inserts weaken(my $weakdbh = $dbh); $dbh->sqlite_update_hook(sub { $weakdbh->{private_mojo_last_insert_id} = $_[3] if $_[0] == DBD::SQLite::INSERT; }); $self->emit(connection => $dbh); return $dbh; } sub _enqueue { my ($self, $dbh) = @_; if (my $parent = $self->parent) { return $parent->_enqueue($dbh) } my $queue = $self->{queue} ||= []; push @$queue, $dbh if $dbh->{Active}; shift @$queue while @$queue > $self->max_connections; } sub _prepare { my $self = shift; # Automatic migrations ++$self->{migrated} and $self->migrations->migrate if !$self->{migrated} && $self->auto_migrate; my $parent = $self->parent; return $parent ? $parent->_prepare : $self->_dequeue; } sub _tempfile { catfile(shift->{tempdir} = File::Temp->newdir, 'sqlite.db') } sub _url_from_file { my $url = URI::db->new; $url->engine('sqlite'); $url->dbname(shift); if (my $options = shift) { $url->query_form($options) } return $url; } 1; =head1 NAME Mojo::SQLite - A tiny Mojolicious wrapper for SQLite =head1 SYNOPSIS use Mojo::SQLite; # Select the library version my $sql = Mojo::SQLite->new('sqlite:test.db'); say $sql->db->query('select sqlite_version() as version')->hash->{version}; # Use migrations to create a table $sql->migrations->name('my_names_app')->from_string(<migrate; -- 1 up create table names (id integer primary key autoincrement, name text); -- 1 down drop table names; EOF # Use migrations to drop and recreate the table $sql->migrations->migrate(0)->migrate; # Get a database handle from the cache for multiple queries my $db = $sql->db; # Use SQL::Abstract to generate simple CRUD queries for you $db->insert('names', {name => 'Isabel'}); my $id = $db->select('names', ['id'], {name => 'Isabel'})->hash->{id}; $db->update('names', {name => 'Bel'}, {id => $id}); $db->delete('names', {name => 'Bel'}); # Insert a few rows in a transaction with SQL and placeholders eval { my $tx = $db->begin; $db->query('insert into names (name) values (?)', 'Sara'); $db->query('insert into names (name) values (?)', 'Stefan'); $tx->commit; }; say $@ if $@; # Insert another row with SQL::Abstract and return the generated id say $db->insert('names', {name => 'Daniel'})->last_insert_id; # JSON roundtrip say $db->query('select ? as foo', {json => {bar => 'baz'}}) ->expand(json => 'foo')->hash->{foo}{bar}; # Select one row at a time my $results = $db->query('select * from names'); while (my $next = $results->hash) { say $next->{name}; } # Select all rows with SQL::Abstract say $_->{name} for $db->select('names')->hashes->each; =head1 DESCRIPTION L is a tiny wrapper around L that makes L a lot of fun to use with the L real-time web framework. Use all L SQLite has to offer, generate CRUD queries from data structures, and manage your database schema with migrations. =head1 BASICS Database and statement handles are cached automatically, so they can be reused transparently to increase performance. And you can handle connection timeouts gracefully by holding on to them only for short amounts of time. use Mojolicious::Lite; use Mojo::SQLite; helper sqlite => sub { state $sql = Mojo::SQLite->new('sqlite:test.db') }; get '/' => sub ($c) { my $db = $c->sqlite->db; $c->render(json => $db->query(q{select datetime('now','localtime') as now})->hash); }; app->start; In this example application, we create a C helper to store a L object. Our action calls that helper and uses the method L to dequeue a L object from the connection pool. Then we use the method L to execute an L statement, which returns a L object. And finally we call the method L to retrieve the first row as a hash reference. All I/O and queries are performed synchronously, and SQLite's default journal mode only supports concurrent reads from multiple processes while the database is not being written. The "Write-Ahead Log" journal mode allows multiple processes to read and write concurrently to the same database file (but only one can write at a time). WAL mode is enabled by the C option, currently enabled by default, and persists when opening that same database in the future. # Performed concurrently (concurrent with writing only with WAL journaling mode) my $pid = fork || die $!; say $sql->db->query(q{select datetime('now','localtime') as time})->hash->{time}; exit unless $pid; The C option prevents WAL mode from being enabled in new databases but doesn't affect databases where it has already been enabled. C may not be set by default in a future release. See L and L for more information. The L is disabled for all connections since Mojo::SQLite 3.003; use single quotes for string literals and double quotes for identifiers, as is normally recommended. All cached database handles will be reset automatically if a new process has been forked, this allows multiple processes to share the same L object safely. Any database errors will throw an exception as C is automatically enabled, so use C or L to catch them. This makes transactions with L easy. While passing a file path of C<:memory:> (or a custom L with C) will create a temporary database, in-memory databases cannot be shared between connections, so subsequent calls to L may return connections to completely different databases. For a temporary database that can be shared between connections and processes, pass a file path of C<:temp:> to store the database in a temporary directory (this is the default), or consider constructing a temporary directory yourself with L if you need to reuse the filename. A temporary directory allows SQLite to create L safely. use File::Spec::Functions 'catfile'; use File::Temp; use Mojo::SQLite; my $tempdir = File::Temp->newdir; # Deleted when object goes out of scope my $tempfile = catfile $tempdir, 'test.db'; my $sql = Mojo::SQLite->new->from_filename($tempfile); =head1 EXAMPLES This distribution also contains a well-structured example L you can use for inspiration. This application shows how to apply the MVC design pattern in practice. =head1 EVENTS L inherits all events from L and can emit the following new ones. =head2 connection $sql->on(connection => sub ($sql, $dbh) { $dbh->do('pragma journal_size_limit=1000000'); }); Emitted when a new database connection has been established. =head1 ATTRIBUTES L implements the following attributes. =head2 abstract my $abstract = $sql->abstract; $sql = $sql->abstract(SQL::Abstract->new); L object used to generate CRUD queries for L, defaults to a L object with C set to C<.> and C set to C<">. # Generate WHERE clause and bind values my($stmt, @bind) = $sql->abstract->where({foo => 'bar', baz => 'yada'}); L provides additional features to the L query methods in L such as C<-json> and C/C. The C feature is not applicable to SQLite queries. $sql->db->select(['some_table', ['other_table', foo_id => 'id']], ['foo', [bar => 'baz'], \q{datetime('now') as dt}], {foo => 'value'}, {order_by => 'foo', limit => 10, offset => 5, group_by => ['foo'], having => {baz => 'value'}}); # Upsert supported since SQLite 3.24.0 $sql->db->insert('some_table', {name => $name, value => $value}, {on_conflict => [name => {value => \'"excluded"."value"'}]}); =head2 auto_migrate my $bool = $sql->auto_migrate; $sql = $sql->auto_migrate($bool); Automatically migrate to the latest database schema with L, as soon as L has been called for the first time. =head2 database_class my $class = $sql->database_class; $sql = $sql->database_class('MyApp::Database'); Class to be used by L, defaults to L. Note that this class needs to have already been loaded before L is called. =head2 dsn my $dsn = $sql->dsn; $sql = $sql->dsn('dbi:SQLite:uri=file:foo.db'); Data source name, defaults to C followed by a path to a temporary file. =head2 max_connections my $max = $sql->max_connections; $sql = $sql->max_connections(3); Maximum number of idle database handles to cache for future use, defaults to C<1>. =head2 migrations my $migrations = $sql->migrations; $sql = $sql->migrations(Mojo::SQLite::Migrations->new); L object you can use to change your database schema more easily. # Load migrations from file and migrate to latest version $sql->migrations->from_file('/home/dbook/migrations.sql')->migrate; =head2 options my $options = $sql->options; $sql = $sql->options({AutoCommit => 1, RaiseError => 1}); Options for database handles, defaults to setting C to C, setting C, C and C, and deactivating C. Note that C and C are considered mandatory, so deactivating them would be very dangerous. See L and L for more information on available options. =head2 parent my $parent = $sql->parent; $sql = $sql->parent(Mojo::SQLite->new); Another L object to use for connection management, instead of establishing and caching our own database connections. =head1 METHODS L inherits all methods from L and implements the following new ones. =head2 new my $sql = Mojo::SQLite->new; my $sql = Mojo::SQLite->new('file:test.db); my $sql = Mojo::SQLite->new('sqlite:test.db'); my $sql = Mojo::SQLite->new(Mojo::SQLite->new); Construct a new L object and parse connection string with L if necessary. # Customize configuration further my $sql = Mojo::SQLite->new->dsn('dbi:SQLite:dbname=test.db'); my $sql = Mojo::SQLite->new->dsn('dbi:SQLite:uri=file:test.db?mode=memory'); # Pass filename directly my $sql = Mojo::SQLite->new->from_filename($filename); =head2 db my $db = $sql->db; Get a database object based on L (which is usually L) for a cached or newly established database connection. The L database handle will be automatically cached again when that object is destroyed, so you can handle problems like connection timeouts gracefully by holding on to it only for short amounts of time. # Add up all the money say $sql->db->select('accounts') ->hashes->reduce(sub { $a->{money} + $b->{money} }); =head2 from_filename $sql = $sql->from_filename('C:\\Documents and Settings\\foo & bar.db', $options); Parse database filename directly. Unlike L, the filename is parsed as a local filename and not a URL. A hashref of L may be passed as the second argument. # Absolute filename $sql->from_filename('/home/fred/data.db'); # Relative to current directory $sql->from_filename('data.db'); # Temporary file database (default) $sql->from_filename(':temp:'); # In-memory temporary database (single connection only) my $db = $sql->from_filename(':memory:')->db; # Additional options $sql->from_filename($filename, { PrintError => 1 }); # Readonly connection without WAL mode $sql->from_filename($filename, { ReadOnly => 1, no_wal => 1 }); # Strict unicode strings and WAL mode use DBD::SQLite::Constants ':dbd_sqlite_string_mode'; $sql->from_filename($filename, { sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_STRICT, wal_mode => 1 }); =head2 from_string $sql = $sql->from_string('test.db'); $sql = $sql->from_string('file:test.db'); $sql = $sql->from_string('file:///C:/foo/bar.db'); $sql = $sql->from_string('sqlite:C:%5Cfoo%5Cbar.db'); $sql = $sql->from_string(Mojo::SQLite->new); Parse configuration from connection string or use another L object as L. Connection strings are parsed as URLs, so you should construct them using a module like L, L, or L. For portability on non-Unix-like systems, either construct the URL with the C scheme, or use L to construct a URL with the C scheme. A URL with no scheme will be parsed as a C URL, and C URLs are parsed according to the current operating system. If specified, the hostname must be C. If the URL has a query string, it will be parsed and applied to L. # Absolute filename $sql->from_string('sqlite:////home/fred/data.db'); $sql->from_string('sqlite://localhost//home/fred/data.db'); $sql->from_string('sqlite:/home/fred/data.db'); $sql->from_string('file:///home/fred/data.db'); $sql->from_string('file://localhost/home/fred/data.db'); $sql->from_string('file:/home/fred/data.db'); $sql->from_string('///home/fred/data.db'); $sql->from_string('//localhost/home/fred/data.db'); $sql->from_string('/home/fred/data.db'); # Relative to current directory $sql->from_string('sqlite:data.db'); $sql->from_string('file:data.db'); $sql->from_string('data.db'); # Connection string must be a valid URL $sql->from_string(Mojo::URL->new->scheme('sqlite')->path($filename)); $sql->from_string(URI::db->new->Mojo::Base::tap(engine => 'sqlite')->Mojo::Base::tap(dbname => $filename)); $sql->from_string(URI::file->new($filename)); # Temporary file database (default) $sql->from_string(':temp:'); # In-memory temporary database (single connection only) my $db = $sql->from_string(':memory:')->db; # Additional options $sql->from_string('data.db?PrintError=1&sqlite_allow_multiple_statements=1'); $sql->from_string(Mojo::URL->new->scheme('sqlite')->path($filename)->query(sqlite_see_if_its_a_number => 1)); $sql->from_string(URI::file->new($filename)->Mojo::Base::tap(query_form => {PrintError => 1})); # Readonly connection without WAL mode $sql->from_string('data.db?ReadOnly=1&no_wal=1'); # String unicode strings and WAL mode use DBD::SQLite::Constants ':dbd_sqlite_string_mode'; $sql->from_string(Mojo::URL->new->scheme('sqlite')->path('data.db') ->query(sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_STRICT, wal_mode => 1)); =head1 DEBUGGING You can set the C environment variable to get some advanced diagnostics information printed by L. DBI_TRACE=1 DBI_TRACE=15 DBI_TRACE=SQL =head1 REFERENCE This is the class hierarchy of the L distribution. =over 2 =item * L =item * L =item * L =item * L =item * L =back =head1 BUGS Report any issues on the public bugtracker. =head1 AUTHOR Dan Book, C =head1 CREDITS Sebastian Riedel, author of L, which this distribution is based on. =head1 COPYRIGHT AND LICENSE Copyright 2015, Dan Book. This library is free software; you may redistribute it and/or modify it under the terms of the Artistic License version 2.0. =head1 SEE ALSO L, L, L sqlite_lite_app.t100644001750001750 213414150724762 17467 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/tuse Mojo::Base -strict; BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } use Test::More; use Mojo::SQLite; use Mojolicious::Lite; use Scalar::Util 'refaddr'; use Test::Mojo; helper sqlite => sub { state $sql = Mojo::SQLite->new }; app->sqlite->db->query('create table if not exists app_test (stuff text)'); app->sqlite->db->query('insert into app_test values (?)', 'I ♥ Mojolicious!'); get '/blocking' => sub { my $c = shift; my $db = $c->sqlite->db; $c->res->headers->header('X-Ref' => refaddr $db->dbh); $c->render(text => $db->query('select * from app_test')->hash->{stuff}); }; my $t = Test::Mojo->new; subtest 'Make sure migrations are not served as static files' => sub { $t->get_ok('/app_test')->status_is(404); }; subtest 'Blocking select (with connection reuse)' => sub { $t->get_ok('/blocking')->status_is(200)->content_is('I ♥ Mojolicious!'); my $ref = $t->tx->res->headers->header('X-Ref'); $t->get_ok('/blocking')->status_is(200)->header_is('X-Ref', $ref) ->content_is('I ♥ Mojolicious!'); $t->app->sqlite->db->query('drop table app_test'); }; done_testing(); 00-report-prereqs.t100644001750001750 1347614150724762 17553 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/t#!perl use strict; use warnings; # This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.028 use Test::More tests => 1; use Module::Metadata; use File::Spec; # from $version::LAX my $lax_version_re = qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )? | (?:\.[0-9]+) (?:_[0-9]+)? ) | (?: v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )? | (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)? ) )/x; # hide optional CPAN::Meta modules from prereq scanner # and check if they are available my $cpan_meta = "CPAN::Meta"; my $cpan_meta_pre = "CPAN::Meta::Prereqs"; my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic # Verify requirements? my $DO_VERIFY_PREREQS = 1; sub _max { my $max = shift; $max = ( $_ > $max ) ? $_ : $max for @_; return $max; } sub _merge_prereqs { my ($collector, $prereqs) = @_; # CPAN::Meta::Prereqs object if (ref $collector eq $cpan_meta_pre) { return $collector->with_merged_prereqs( CPAN::Meta::Prereqs->new( $prereqs ) ); } # Raw hashrefs for my $phase ( keys %$prereqs ) { for my $type ( keys %{ $prereqs->{$phase} } ) { for my $module ( keys %{ $prereqs->{$phase}{$type} } ) { $collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module}; } } } return $collector; } my @include = qw( ); my @exclude = qw( ); # Add static prereqs to the included modules list my $static_prereqs = do './t/00-report-prereqs.dd'; # Merge all prereqs (either with ::Prereqs or a hashref) my $full_prereqs = _merge_prereqs( ( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ), $static_prereqs ); # Add dynamic prereqs to the included modules list (if we can) my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; my $cpan_meta_error; if ( $source && $HAS_CPAN_META && (my $meta = eval { CPAN::Meta->load_file($source) } ) ) { $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs); } else { $cpan_meta_error = $@; # capture error from CPAN::Meta->load_file($source) $source = 'static metadata'; } my @full_reports; my @dep_errors; my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs; # Add static includes into a fake section for my $mod (@include) { $req_hash->{other}{modules}{$mod} = 0; } for my $phase ( qw(configure build test runtime develop other) ) { next unless $req_hash->{$phase}; next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING}); for my $type ( qw(requires recommends suggests conflicts modules) ) { next unless $req_hash->{$phase}{$type}; my $title = ucfirst($phase).' '.ucfirst($type); my @reports = [qw/Module Want Have/]; for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) { next if $mod eq 'perl'; next if grep { $_ eq $mod } @exclude; my $file = $mod; $file =~ s{::}{/}g; $file .= ".pm"; my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC; my $want = $req_hash->{$phase}{$type}{$mod}; $want = "undef" unless defined $want; $want = "any" if !$want && $want == 0; my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required"; if ($prefix) { my $have = Module::Metadata->new_from_file( File::Spec->catfile($prefix, $file) )->version; $have = "undef" unless defined $have; push @reports, [$mod, $want, $have]; if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) { if ( $have !~ /\A$lax_version_re\z/ ) { push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)"; } elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) { push @dep_errors, "$mod version '$have' is not in required range '$want'"; } } } else { push @reports, [$mod, $want, "missing"]; if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) { push @dep_errors, "$mod is not installed ($req_string)"; } } } if ( @reports ) { push @full_reports, "=== $title ===\n\n"; my $ml = _max( map { length $_->[0] } @reports ); my $wl = _max( map { length $_->[1] } @reports ); my $hl = _max( map { length $_->[2] } @reports ); if ($type eq 'modules') { splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports; } else { splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports; } push @full_reports, "\n"; } } } if ( @full_reports ) { diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports; } if ( $cpan_meta_error || @dep_errors ) { diag "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n"; } if ( $cpan_meta_error ) { my ($orig_source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; diag "\nCPAN::Meta->load_file('$orig_source') failed with: $cpan_meta_error\n"; } if ( @dep_errors ) { diag join("\n", "\nThe following REQUIRED prerequisites were not satisfied:\n", @dep_errors, "\n" ); } pass('Reported prereqs'); # vim: ts=4 sts=4 sw=4 et: migrations000755001750001750 014150724762 16140 5ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/ttest.sql100644001750001750 41214150724762 17755 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/t/migrations-- 1 up create table if not exists migration_test_three (baz text); -- 1 down drop table if exists migration_test_three; -- 2 up insert into migration_test_three values ('just'); insert into migration_test_three values ('works ♥'); -- 3 up -- 4 up does_not_exist; author000755001750001750 014150724762 15456 5ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/xtpod-syntax.t100644001750001750 25214150724762 20070 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/xt/author#!perl # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use strict; use warnings; use Test::More; use Test::Pod 1.41; all_pod_files_ok(); 00-report-prereqs.dd100644001750001750 344414150724762 17651 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/tdo { my $x = { 'configure' => { 'requires' => { 'Module::Build::Tiny' => '0.034' } }, 'develop' => { 'requires' => { 'Pod::Coverage::TrustPod' => '0', 'Test::Pod' => '1.41', 'Test::Pod::Coverage' => '1.08' } }, 'runtime' => { 'requires' => { 'Carp' => '0', 'DBD::SQLite' => '1.68', 'DBI' => '1.627', 'File::Spec::Functions' => '0', 'File::Temp' => '0', 'Mojolicious' => '8.03', 'SQL::Abstract::Pg' => '1.0', 'Scalar::Util' => '0', 'URI' => '1.69', 'URI::db' => '0.15', 'URI::file' => '4.21', 'perl' => '5.010001' } }, 'test' => { 'recommends' => { 'CPAN::Meta' => '2.120900' }, 'requires' => { 'File::Spec' => '0', 'Module::Metadata' => '0', 'Test::More' => '0.96' } } }; $x; }t000755001750001750 014150724762 16525 5ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/examples/blogblog.t100644001750001750 346114150724762 20001 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/examples/blog/tuse Mojo::Base -strict; use Test::More; use Mojo::SQLite; use Mojo::URL; use Test::Mojo; # Override configuration for testing my $t = Test::Mojo->new(Blog => {sqlite => ':temp:', secrets => ['test_s3cret']}); $t->ua->max_redirects(10); # No posts yet $t->get_ok('/')->status_is(200)->text_is('title' => 'Blog') ->text_is('body > a' => 'New post')->element_exists_not('h2'); # Create a new post $t->get_ok('/posts/create')->status_is(200)->text_is('title' => 'New post') ->element_exists('form input[name=title]') ->element_exists('form textarea[name=body]'); $t->post_ok('/posts' => form => {title => 'Testing', body => 'This is a test.'}) ->status_is(200)->text_is('title' => 'Testing')->text_is('h2' => 'Testing') ->text_like('p' => qr/This is a test/); # Read the post $t->get_ok('/')->status_is(200)->text_is('title' => 'Blog') ->text_is('h2 a' => 'Testing')->text_like('p' => qr/This is a test/); $t->get_ok('/posts/1')->status_is(200)->text_is('title' => 'Testing') ->text_is('h2' => 'Testing')->text_like('p' => qr/This is a test/) ->text_is('body > a' => 'Edit'); # Update the post $t->get_ok('/posts/1/edit')->status_is(200)->text_is('title' => 'Edit post') ->element_exists('form input[name=title][value=Testing]') ->text_like('form textarea[name=body]' => qr/This is a test/) ->element_exists('form input[value=Remove]'); $t->post_ok( '/posts/1?_method=PUT' => form => {title => 'Again', body => 'It works.'}) ->status_is(200)->text_is('title' => 'Again')->text_is('h2' => 'Again') ->text_like('p' => qr/It works/); $t->get_ok('/posts/1')->status_is(200)->text_is('title' => 'Again') ->text_is('h2' => 'Again')->text_like('p' => qr/It works/); # Delete the post $t->post_ok('/posts/1?_method=DELETE')->status_is(200) ->text_is('title' => 'Blog')->element_exists_not('h2'); done_testing(); blog000755001750001750 014150724762 16262 5ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/examplesblog.conf100644001750001750 6514150724762 20155 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/examples/blog{ sqlite => 'test.db', secrets => ['s3cret'], } pod-coverage.t100644001750001750 33414150724762 20336 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/xt/author#!perl # This file was automatically generated by Dist::Zilla::Plugin::PodCoverageTests. use Test::Pod::Coverage 1.08; use Pod::Coverage::TrustPod; all_pod_coverage_ok({ coverage_class => 'Pod::Coverage::TrustPod' }); lib000755001750001750 014150724762 17030 5ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/examples/blogBlog.pm100644001750001750 217714150724762 20420 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/examples/blog/libpackage Blog; use Mojo::Base 'Mojolicious'; use Blog::Model::Posts; use Mojo::SQLite; sub startup { my $self = shift; # Configuration $self->plugin('Config'); $self->secrets($self->config('secrets')); # Model $self->helper(sqlite => sub { state $sql = Mojo::SQLite->new->from_filename(shift->config('sqlite')) }); $self->helper( posts => sub { state $posts = Blog::Model::Posts->new(sqlite => shift->sqlite) }); # Migrate to latest version if necessary my $path = $self->home->child('migrations', 'blog.sql'); $self->sqlite->auto_migrate(1)->migrations->name('blog')->from_file($path); # Controller my $r = $self->routes; $r->get('/' => sub { shift->redirect_to('posts') }); $r->get('/posts')->to('posts#index'); $r->get('/posts/create')->to('posts#create')->name('create_post'); $r->post('/posts')->to('posts#store')->name('store_post'); $r->get('/posts/:id')->to('posts#show')->name('show_post'); $r->get('/posts/:id/edit')->to('posts#edit')->name('edit_post'); $r->put('/posts/:id')->to('posts#update')->name('update_post'); $r->delete('/posts/:id')->to('posts#remove')->name('remove_post'); } 1; script000755001750001750 014150724762 17566 5ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/examples/blogblog100755001750001750 34514150724762 20561 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/examples/blog/script#!/usr/bin/env perl use strict; use warnings; use FindBin; BEGIN { unshift @INC, "$FindBin::Bin/../lib" } # Start command line interface for application require Mojolicious::Commands; Mojolicious::Commands->start_app('Blog'); SQLite000755001750001750 014150724762 16334 5ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/lib/MojoPubSub.pm100644001750001750 146014150724762 20233 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/lib/Mojo/SQLitepackage Mojo::SQLite::PubSub; use Mojo::Base -strict; use Mojo::Util 'deprecated'; our $VERSION = '3.008'; deprecated 'Mojo::SQLite::PubSub is deprecated and should no longer be used'; 1; =encoding utf8 =head1 NAME Mojo::SQLite::PubSub - (DEPRECATED) Publish/Subscribe =head1 DESCRIPTION L is DEPRECATED and now an empty package. It was originally written as a toy following the API of L, but as SQLite is serverless and has no ability to notify clients, it is not possible to implement an efficient pubsub system as in for example PostgreSQL, Redis, or websockets. Consider instead using the pubsub facilities of L, L, or L. =head1 SEE ALSO L, L, L =for Pod::Coverage *EVERYTHING* Results.pm100644001750001750 1251314150724762 20515 0ustar00grinnzgrinnz000000000000Mojo-SQLite-3.008/lib/Mojo/SQLitepackage Mojo::SQLite::Results; use Mojo::Base -base; use Mojo::Collection; use Mojo::JSON 'from_json'; use Mojo::Util 'tablify'; our $VERSION = '3.008'; has [qw(db sth)]; sub new { my $self = shift->SUPER::new(@_); ($self->{sth}{private_mojo_refcount} //= 0)++; return $self; } sub DESTROY { my $self = shift; return() unless my $sth = $self->{sth}; $sth->finish unless --$sth->{private_mojo_refcount}; } sub array { ($_[0]->_expand($_[0]->sth->fetchrow_arrayref))[0] } sub arrays { _collect($_[0]->_expand(@{$_[0]->sth->fetchall_arrayref})) } sub columns { shift->sth->{NAME} } sub expand { my ($self, %expands) = @_; for my $type (keys %expands) { my @cols = ref $expands{$type} eq 'ARRAY' ? @{$expands{$type}} : $expands{$type}; ++$self->{expand}{$type}{$_} for @cols; } return $self; } sub finish { shift->sth->finish } sub hash { ($_[0]->_expand($_[0]->sth->fetchrow_hashref))[0] } sub hashes { _collect($_[0]->_expand(@{$_[0]->sth->fetchall_arrayref({})})) } sub last_insert_id { shift->{last_insert_id} // 0 } sub rows { shift->sth->rows } sub text { tablify shift->arrays } sub _collect { Mojo::Collection->new(@_) } sub _expand { my ($self, @rows) = @_; return @rows unless $self->{expand} and $rows[0]; if (ref $rows[0] eq 'HASH') { my @json_names = keys %{$self->{expand}{json}}; for my $r (@rows) { $r->{$_} = from_json $r->{$_} for grep { $r->{$_} } @json_names } } else { my $cols = $self->columns; my @json_idxs = grep { $self->{expand}{json}{$cols->[$_]} } 0..$#$cols; for my $r (@rows) { $r->[$_] = from_json $r->[$_] for grep { $r->[$_] } @json_idxs } } return @rows; } 1; =head1 NAME Mojo::SQLite::Results - Results =head1 SYNOPSIS use Mojo::SQLite::Results; my $results = Mojo::SQLite::Results->new(sth => $sth); $results->hashes->map(sub { $_->{foo} })->shuffle->join("\n")->say; =head1 DESCRIPTION L is a container for L statement handles used by L. =head1 ATTRIBUTES L implements the following attributes. =head2 db my $db = $results->db; $results = $results->db(Mojo::SQLite::Database->new); L object these results belong to. =head2 sth my $sth = $results->sth; $results = $results->sth($sth); L statement handle results are fetched from. =head1 METHODS L inherits all methods from L and implements the following new ones. =head2 new my $results = Mojo::SQLite::Results->new; my $results = Mojo::SQLite::Results->new(sth => $sth); my $results = Mojo::SQLite::Results->new({sth => $sth}); Construct a new L object. =head2 array my $array = $results->array; Fetch next row from L and return it as an array reference. Note that L needs to be called if you are not fetching all the possible rows. # Process one row at a time while (my $next = $results->array) { say $next->[3]; } =head2 arrays my $collection = $results->arrays; Fetch all rows from L and return them as a L object containing array references. # Process all rows at once say $results->arrays->reduce(sub { $a + $b->[3] }, 0); =head2 columns my $columns = $results->columns; Return column names as an array reference. # Names of all columns say for @{$results->columns}; =head2 expand $results = $results->expand(json => 'some_json'); $results = $results->expand(json => ['some_json','other_json']); Decode specified fields from a particular format to Perl values for all rows. Currently only the C text format is recognized. The names must exactly match the column names as returned by L; it is recommended to use explicit aliases in the query for consistent column names. # Expand JSON $results->expand(json => 'json_field')->hashes->map(sub { $_->{foo}{bar} })->join("\n")->say; =head2 finish $results->finish; Indicate that you are finished with L and will not be fetching all the remaining rows. =head2 hash my $hash = $results->hash; Fetch next row from L and return it as a hash reference. Note that L needs to be called if you are not fetching all the possible rows. # Process one row at a time while (my $next = $results->hash) { say $next->{money}; } =head2 hashes my $collection = $results->hashes; Fetch all rows from L and return them as a L object containing hash references. # Process all rows at once say $results->hashes->reduce(sub { $a + $b->{money} }, 0); =head2 last_insert_id my $id = $results->last_insert_id; Returns the L of the most recent successful C. =head2 rows my $num = $results->rows; Number of rows. Note that for C statement with L (usually an L object) and execute it with L. You can also append a callback for API compatibility with L; the query is still executed in a blocking manner. $db->select(some_table => ['foo'] => {bar => 'yada'} => sub ($db, $err, $results) { ... }); Mojo::IOLoop->start unless Mojo::IOLoop->is_running; Use all the same argument variations you would pass to the C