murmuration 2.0.36 → 2.0.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +87 -32
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -9,9 +9,11 @@ There are two specific packages that you should make use of instead this one:
9
9
 
10
10
  This readme file pertains to both, although there are specific instructions for each of the above in their respective readme files.
11
11
 
12
- Murmuration can be used as alternative to a database [ORM](https://en.wikipedia.org/wiki/Object-relational_mapping). It is deliberately simple and low level in the sense that it provides no more than the bare minimum functionality needed to connect to a database and generate statements to be executed ideally in the context of transactions.
12
+ Murmuration can be used as alternative to a database [ORM](https://en.wikipedia.org/wiki/Object-relational_mapping).
13
+ It is deliberately simple and low level in the sense that it provides no more than the bare minimum functionality needed to connect to a database and generate statements to be executed ideally in the context of transactions.
13
14
 
14
- There is also some adjunct migration functionality that may or may not suit your use case. If used as prescribed then it guarantees that an application remains in line with its database, as the latter can be migrated if needed each time the former is deployed.
15
+ There is also some adjunct migration functionality that may or may not suit your use case.
16
+ If used as prescribed then it guarantees that an application remains in line with its database, as the latter can be migrated if needed each time the former is deployed.
15
17
 
16
18
  ## Installation
17
19
 
@@ -31,16 +33,23 @@ Remember that it is the aforementioned specific packages that you should install
31
33
 
32
34
  ## Usage
33
35
 
34
- Statements are covered first up, but ideally they should be executed in the context of transactions and therefore the remainder of this section cannot be overlooked. In particular, the example statements that follow are executed inside operations, which are themselves covered in the later subsection. If you do not want to use operations then you can in fact easily wrap statements in promises, again see the relevant subsection later on.
36
+ Statements are covered first up, but ideally they should be executed in the context of transactions and therefore the remainder of this section cannot be overlooked.
37
+ In particular, the example statements that follow are executed inside operations, which are themselves covered in the later subsection.
38
+ If you do not want to use operations then you can in fact easily wrap statements in promises, again see the relevant subsection later on.
35
39
 
36
40
  ### Generating statements
37
41
 
38
- Statements are generated dynamically with a simple, promise-like syntax. Again it is worth noting that they can be executed inside either operations or promises. In the examples below operations are used. Wrapping statement in promises is covered later on.
42
+ Statements are generated dynamically with a simple, promise-like syntax.
43
+ Again it is worth noting that they can be executed inside either operations or promises.
44
+ In the examples below operations are used. Wrapping statement in promises is covered later on.
39
45
 
40
46
  Note that the following points apply to all of the examples:
41
47
 
42
- * A local `using()` function has been employed rather the function supplied by the package, because the `Statement` class it utilities has been extended. See the relevant subsection below for more details.
43
- * Each operation provides a connection and a context as well as the `abort`, `proceed` and `compoete` callbacks. These allow the result of the statement's execution to determine whether the application should abort, proceed to the next operation or complete, respectively. Again see the relevant section on operations later on.
48
+ * A local `using()` function has been employed rather the function supplied by the package, because the `Statement` class it utilities has been extended.
49
+ See the relevant subsection below for more details.
50
+ * Each operation provides a connection and a context as well as the `abort`, `proceed` and `compoete` callbacks.
51
+ These allow the result of the statement's execution to determine whether the application should abort, proceed to the next operation or complete, respectively.
52
+ * Again see the relevant section on operations later on.
44
53
 
45
54
  In the first example a row is selected should its email address and password match the given values:
46
55
 
@@ -148,13 +157,20 @@ Note:
148
157
 
149
158
  * The `set()` method takes a plain old JavaScript object.
150
159
 
151
- In general, the assumption with passing plain old JavaScript objects is that clauses and so forth can easily be constructed from them. The `set()`, `where()` and `values()` methods can also take appended template literals, however, so that you can define parts of the SQL with more freedom. More detail is given towards the end of the next subsection.
160
+ In general, the assumption with passing plain old JavaScript objects is that clauses and so forth can easily be constructed from them.
161
+ The `set()`, `where()` and `values()` methods can also take appended template literals, however, so that you can define parts of the SQL with more freedom.
162
+ Also, for the sake of simplicity, each of these methods can also take a string, given the most flexibility possible.
163
+ More detail is given towards the end of the next subsection.
152
164
 
153
165
  All of the methods that can be called against the instances of statements returned from the `using()` function are described in the statement specification subsection further below.
154
166
 
167
+
155
168
  ### Operations and transactions
156
169
 
157
- Ideally, all statements should be executed in the context of a transaction. Murmuration provides a `transaction()` function that allows you to do this. It takes `configuration`, `operations`, `callback` and `context` arguments. The callback provided will have a `completed` argument while the context is mandatory and must be a plain old JavaScript object. The `transaction()` function makes use of the `context` object itself, in fact, with the object's `connection`, `operations` and `completed` properties being reserved.
170
+ Ideally, all statements should be executed in the context of a transaction.
171
+ Murmuration provides a `transaction()` function that allows you to do this. It takes `configuration`, `operations`, `callback` and `context` arguments.
172
+ The callback provided will have a `completed` argument while the context is mandatory and must be a plain old JavaScript object.
173
+ The `transaction()` function makes use of the `context` object itself, in fact, with the object's `connection`, `operations` and `completed` properties being reserved.
158
174
 
159
175
  In the example below, three operations have been provided and the context has some example properties that they will make use of:
160
176
 
@@ -178,17 +194,30 @@ transaction(configuration, operations, (completed) => {
178
194
  }, context);
179
195
  ```
180
196
 
181
- The signatures of the operations have already been demonstrated in the examples above. In fact the bodies of the operations are immaterial, they do not have to execute statements and can contain any application logic. However, if you do want to execute statement inside an operation then again see the examples for guidance on how to integration the statement methods with the operation callbacks.
197
+ The signatures of the operations have already been demonstrated in the examples above.
198
+ In fact the bodies of the operations are immaterial, they do not have to execute statements and can contain any application logic.
199
+ However, if you do want to execute statement inside an operation then again see the examples for guidance on how to integration the statement methods with the operation callbacks.
182
200
 
183
- What follows are general prescriptions for how to make use of transactions. They are meant to persuade that the small amount of boilerplate necessary to execute any statement inside a transaction is always worth the effort.
201
+ What follows are general prescriptions for how to make use of transactions.
202
+ They are meant to persuade that the small amount of boilerplate necessary to execute any statement inside a transaction is always worth the effort.
184
203
 
185
- * You can try to insert values into a table and test whether they are unique by whether or not the insert fails. However, the database will throw an error that is indistinguishable from errors that occur because of, say, syntax errors in the SQL. It could be argued that it is bad practice to knowingly run a query that may result in an error and use the presence of such as an indication of whether or not an otherwise correct query has been successful. Therefore a better approach is to precede the insert with a select statement and execute both in the context of a transaction.
204
+ * You can try to insert values into a table and test whether they are unique by whether or not the insert fails.
205
+ However, the database will throw an error that is indistinguishable from errors that occur because of, say, syntax errors in the SQL.
206
+ It could be argued that it is bad practice to knowingly run a query that may result in an error and use the presence of such as an indication of whether or not an otherwise correct query has been successful.
207
+ Therefore a better approach is to precede the insert with a select statement and execute both in the context of a transaction.
186
208
 
187
- * Often conflating operations means that application logic that is far more suited to, in this case, JavaScript must be added to the SQL itself. Or worse, the application logic is simply assumed to be implicit in the SQL. It is far better to implement this kind of logic explicitly in JavaScript than complicate SQL statements with it. In short, executing multiple SQL statements threaded together with SQL variables and the like is always best avoided.
209
+ * Often conflating operations means that application logic that is far more suited to, in this case, JavaScript must be added to the SQL itself.
210
+ Or worse, the application logic is simply assumed to be implicit in the SQL.
211
+ It is far better to implement this kind of logic explicitly in JavaScript than complicate SQL statements with it.
212
+ In short, executing multiple SQL statements threaded together with SQL variables and the like is always best avoided.
188
213
 
189
- * As well as conditional branching, for example, often functionality needs to be implemented in the context of a transaction that cannot simply be added to an SQL statement. Unzipping a stored binary, for example, or checking some program variable dependent upon a prior query. Furthermore, a shared context means that even though several parts of the application logic might be related, they can still effectively communication with one another of the course of the transaction.
214
+ * As well as conditional branching, for example, often functionality needs to be implemented in the context of a transaction that cannot simply be added to an SQL statement.
215
+ Unzipping a stored binary, for example, or checking some program variable dependent upon a prior query.
216
+ Furthermore, a shared context means that even though several parts of the application logic might be related, they can still effectively communication with one another of the course of the transaction.
190
217
 
191
- The example above demonstrates the crux of the approach taken here, therefore. The application logic is to be found in easily readable, atomic form within the body of each operation. On the other hand the SQL statements are considered to be dumb in the sense that they do nothing but slavishly place or retrieve information into or from the database.
218
+ The example above demonstrates the crux of the approach taken here, therefore.
219
+ The application logic is to be found in easily readable, atomic form within the body of each operation.
220
+ On the other hand the SQL statements are considered to be dumb in the sense that they do nothing but slavishly place or retrieve information into or from the database.
192
221
 
193
222
  ### Supporting promises and the `async`/`await` syntax
194
223
 
@@ -315,7 +344,8 @@ Connection.fromConfiguration(configuration, (error, connection) => {
315
344
  };
316
345
  ```
317
346
 
318
- If successful, the `error` argument of the callback will be null and a `connection` object will be returned, otherwise the `error` argument will be truthy. Details of the format of the `configuration` object can be found in the configuration subsections of the specific package readme files.
347
+ If successful, the `error` argument of the callback will be null and a `connection` object will be returned, otherwise the `error` argument will be truthy.
348
+ Details of the format of the `configuration` object can be found in the configuration subsections of the specific package readme files.
319
349
 
320
350
  ### Logging
321
351
 
@@ -337,9 +367,11 @@ If you do not provide a `log` object, all logging is suppressed.
337
367
 
338
368
  ### Error handling
339
369
 
340
- In the event of an error, if a `log` property has been added to the `configuration` object then the `log.error()` function will be called with a message containing a reasonable stab at the cause of the error. Details can be found in the subsections of the same name in the specific package readme files.
370
+ In the event of an error, if a `log` property has been added to the `configuration` object then the `log.error()` function will be called with a message containing a reasonable stab at the cause of the error.
371
+ Details can be found in the subsections of the same name in the specific package readme files.
341
372
 
342
- These messages are meant to help with debugging simple mistakes such as providing incorrect configuration. If you do not find them helpful, do not provide a `log` object and be assured that the errors are always returned by way of callback function arguments for you to deal with as you see fit.
373
+ These messages are meant to help with debugging simple mistakes such as providing incorrect configuration.
374
+ If you do not find them helpful, do not provide a `log` object and be assured that the errors are always returned by way of callback function arguments for you to deal with as you see fit.
343
375
 
344
376
  ### Statement class specification
345
377
 
@@ -347,10 +379,12 @@ The following specification gives a complete and more detailed list of the metho
347
379
 
348
380
  * One of the `selectFrom()`, `insertInto()`, `deleteFrom()` or `delete()` methods must be called. Each takes a string for the name of a table or view.
349
381
 
350
- It is recommended that these methods are not called directly, by the way, rather methods that call them indirectly are created on a subclass of the `Statement` class, in order to avoid repetition. This is covered in the later subsection on extending the `Statement` class.
382
+ It is recommended that these methods are not called directly, by the way, rather methods that call them indirectly are created on a subclass of the `Statement` class, in order to avoid repetition.
383
+ This is covered in the later subsection on extending the `Statement` class.
351
384
 
352
385
  * The `success()` method must accompany the `insertInto()`, `deleteFrom()` or `delete()` methods.
353
- * If the `selectFrom()` method is called, then one of the following of the following methods must also be called. Each takes a callback. In all but the last case, you must also call the `else()` method with a callback:
386
+ * If the `selectFrom()` method is called, then one of the following of the following methods must also be called. Each takes a callback.
387
+ In all but the last case, you must also call the `else()` method with a callback:
354
388
  * The `none()` method for when no rows are returned.
355
389
  * The `one()` method for when exactly one row is returned.
356
390
  * The `first()` method for when one or more rows are returned.
@@ -362,13 +396,17 @@ It is recommended that these methods are not called directly, by the way, rather
362
396
  * The `values()` method must be called along with the `insertInto()` method.
363
397
  * Either the `getSQL()` or `execute()` methods must be called, usually the latter.
364
398
 
365
- Each of the `set()`, `where()` and `values()` methods can take a plain old JavaScript object or an appended template literal. You cannot pass a string as an argument because there is a danger that it might contain an un-escaped value. By forcing you to pass an appended template literal, the method in question is able to pass the array of arguments it receives directly on to the underlying database package, thereby guaranteeing that they will be correctly cast and escaped.
399
+ Each of the `set()`, `where()` and `values()` methods can take a plain old JavaScript object or an appended template literal.
400
+ You cannot pass a string as an argument because there is a danger that it might contain an un-escaped value.
401
+ By forcing you to pass an appended template literal, the method in question is able to pass the array of arguments it receives directly on to the underlying database package, thereby guaranteeing that they will be correctly cast and escaped.
366
402
 
367
403
  ## Migrations
368
404
 
369
- The migration functionality will definitely not suit every use case, however it can provide surety for small applications running on multiple Node instances connecting to a single database instance. It is essential that the prescriptions below are followed pretty much to the letter. Failing to do so will doubtless result in failure.
405
+ The migration functionality will definitely not suit every use case, however it can provide surety for small applications running on multiple Node instances connecting to a single database instance.
406
+ It is essential that the prescriptions below are followed pretty much to the letter. Failing to do so will doubtless result in failure.
370
407
 
371
- The `migrate()` function takes the usual `configuration` argument followed by `migrationsDirectoryPath` argument and a `callback` argument. The callback is called with the usual `error` argument, which is truthy if the migrations have succeeded and falsey otherwise.
408
+ The `migrate()` function takes the usual `configuration` argument followed by `migrationsDirectoryPath` argument and a `callback` argument.
409
+ The callback is called with the usual `error` argument, which is truthy if the migrations have succeeded and falsey otherwise.
372
410
 
373
411
  ```
374
412
  const configuration = ... ,
@@ -381,7 +419,9 @@ migrate(configuration, migrationsDirectoryPath, (error) => {
381
419
  ```
382
420
  If you provide a `log` property in the configuration, you must supply it with `info()` and `debug()` functions as well as an `error()` function.
383
421
 
384
- Within the migrations directory there should be a collection of SQL files each containing a single SQL statement that changes the database schema or data in some way, in other words a migration. The naming convention for the SQL files is that they must start with a positive integer followed by a dash. Further, since this pattern is searched for in the fully qualified file path with a regular expression, the migration directory in indeed any of its parent directories cannot have also have it.
422
+ Within the migrations directory there should be a collection of SQL files each containing a single SQL statement that changes the database schema or data in some way, in other words a migration.
423
+ The naming convention for the SQL files is that they must start with a positive integer followed by a dash.
424
+ Further, since this pattern is searched for in the fully qualified file path with a regular expression, the migration directory in indeed any of its parent directories cannot have also have it.
385
425
 
386
426
  Perhaps a list of the first few migration files in an example application will be more helpful:
387
427
 
@@ -400,27 +440,38 @@ migration -- -- 1-create-user-table.sql
400
440
 
401
441
  ...
402
442
  ```
403
- Aside from the integer-dash format, there is no specific format for the file names needed, however it is recommended that they be both descriptive and consistent, as above. There is no harm in renaming these files, by the way, as long as the numbers remain constant.
443
+ Aside from the integer-dash format, there is no specific format for the file names needed, however it is recommended that they be both descriptive and consistent, as above.
444
+ There is no harm in renaming these files, by the way, as long as the numbers remain constant.
404
445
 
405
- Each time the Node instance on which the application runs is started up, the migration directory will be read and any newly added migration files will be run in order. A table is kept with the number of the previously last executed migration in order to facilitate this.
446
+ Each time the Node instance on which the application runs is started up, the migration directory will be read and any newly added migration files will be run in order.
447
+ A table is kept with the number of the previously last executed migration in order to facilitate this.
406
448
 
407
- It is crucial that any changes to the JavaScript or indeed any parts of the application that rely on a new migration are committed along with it. This ensures that whenever the migration is executed and the database schema or data are changed, the application is in a fit state to make use of it.
449
+ It is crucial that any changes to the JavaScript or indeed any parts of the application that rely on a new migration are committed along with it.
450
+ This ensures that whenever the migration is executed and the database schema or data are changed, the application is in a fit state to make use of it.
408
451
 
409
- Migrations must obviously never, ever be changed once they have been committed. If a mistake has been made, it must only be rectified by way of a subsequent migration. When developing migrations, test exhaustively before committing them. You can at least reverse the effect of a migration by overwriting the database with a dump of the previous version.
452
+ Migrations must obviously never, ever be changed once they have been committed.
453
+ If a mistake has been made, it must only be rectified by way of a subsequent migration.
454
+ When developing migrations, test exhaustively before committing them.
455
+ You can at least reverse the effect of a migration by overwriting the database with a dump of the previous version.
410
456
 
411
457
  If used with care, this migration functionality is wholly effective in the aforementioned use case of a single database instance allied with one or possibly many application instances.
412
458
 
413
459
  ### Custom migrations
414
460
 
415
- As well as migrations based on SQL files it is possible to add custom migrations. These are identified by custom text files that sit next to the SQL files in the migrations directory. They must have the following format:
461
+ As well as migrations based on SQL files it is possible to add custom migrations.
462
+ These are identified by custom text files that sit next to the SQL files in the migrations directory.
463
+ They must have the following format:
416
464
 
417
465
  ```
418
466
  12-CUSTOM.txt
419
467
  ```
420
468
 
421
- That is, the file name must start with a contiguous version number followed by a hyphen, but end with `CUSTOM` and have a `.txt` extension. Any other format will be ignored and will also lead to all further migrations being ignored. The contents of these files can be left empty or can contain information pertinent to the custom migration.
469
+ That is, the file name must start with a contiguous version number followed by a hyphen, but end with `CUSTOM` and have a `.txt` extension.
470
+ Any other format will be ignored and will also lead to all further migrations being ignored.
471
+ The contents of these files can be left empty or can contain information pertinent to the custom migration.
422
472
 
423
- To create a custom migration, extend the CustomMigration class and fill out the `apply()` method as needed. This example requires the CustomMigration class from the Murmuration-PostGreSQL package but the same holds for the Murmuration-MariaDB package:
473
+ To create a custom migration, extend the CustomMigration class and fill out the `apply()` method as needed.
474
+ This example requires the CustomMigration class from the Murmuration-PostGreSQL package but the same holds for the Murmuration-MariaDB package:
424
475
 
425
476
  ```
426
477
  const { CustomMigration } = require("murmuration-postgresql");
@@ -439,9 +490,13 @@ class IdentifierCustomMigration extends CustomMigration {
439
490
  }
440
491
  }
441
492
  ```
442
- Note that the `apply()` method takes `connection` and `callback` arguments. The latter must be called with a boolean `error` argument when the migration completes. Also note the format of the static `fromFilePath()` factory method. The file path of the text file is available via a standard `getFilePath()` method, by the way.
493
+ Note that the `apply()` method takes `connection` and `callback` arguments.
494
+ The latter must be called with a boolean `error` argument when the migration completes.
495
+ Also note the format of the static `fromFilePath()` factory method.
496
+ The file path of the text file is available via a standard `getFilePath()` method, by the way.
443
497
 
444
- Custom migrations must be collected together into a map, the keys of which are precisely the file names of the custom text files in the migrations SQL directory. For example:
498
+ Custom migrations must be collected together into a map, the keys of which are precisely the file names of the custom text files in the migrations SQL directory.
499
+ For example:
445
500
 
446
501
  ```
447
502
  const IdentifierCustomMigration = rquire("./identifierCustomMigration");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "murmuration",
3
3
  "author": "James Smith",
4
- "version": "2.0.36",
4
+ "version": "2.0.38",
5
5
  "license": "MIT, Anti-996",
6
6
  "homepage": "https://github.com/djalbat/murmuration",
7
7
  "description": "Database statements, transactions and migrations.",