NetCDF: Network Common Data Form - Vector
New in version 2.1.
Driver short name
netCDF
Build dependencies
libnetcdf
The netCDF driver supports read and write (creation from scratch and in some cases append operations) to vector datasets (you can find documentation for the raster side)
NetCDF is an interface for array-oriented data access and is used for representing scientific data.
The driver handles the "point", "profile" and "trajectory" (single trajectory) feature types of the CF 1.6 convention. For CF-1.7 and below (as well as non-CF files), it also supports a more custom approach for non-point geometries.
The driver also supports writing and reading from CF-1.8 convention compliant files that have simple geometry information encoded within them.
Driver capabilities
Supports Create()
This driver supports the GDALDriver::Create()
operation
Supports Georeferencing
This driver supports georeferencing
Supports VirtualIO
This driver supports virtual I/O operations (/vsimem/, etc.)
Conventions and Data Formats
The netCDF vector driver supports reading and writing netCDF files following the Climate and Forecast (CF) Metadata Conventions. Vector datasets can be written using the simple geometry specification of the CF-1.8 convention, or by using the CF-1.6 convention and by writing non-point geometry items as WKT.
Distinguishing the Two Formats
Upon reading a netCDF file, the driver will attempt to read the global Conventions attribute. If i's value is CF-1.8 or higher (in this exact format, as specified in the CF convention), then the driver will check if the netCDF file has CF-1.8 geometries contained within it. If the Conventions attribute has a value lower than CF-1.8 or no CF-1.8 geometries, the driver will try to detect "point", "profile" and "trajectory" (single trajectory) feature types.
CF-1.8 Writing Limitations
Writing to a CF-1.8 netCDF dataset poses some limitations. Only writing the feature types specified by the CF-1.8 standard (see section Geometry for more details) are supported, and measured features are only partially supported. Other geometries, such as non-simple curve geometries, are not supported in any way.
CF-1.8 datasets also do not support the append access mode.
There are what are considered reserved variable names for CF-1.8 datasets. These variable names are used by the driver to store its metadata. Refrain from using these names as layer names to avoid naming conflicts when writing datasets with multiple layers.
Suppose a layer in a CF-1.8 dataset has the name LAYER with a field with name FIELD. Then the following names would be considered reserved:
LAYER_node_coordinates: used to store point information
LAYER_node_count: used to store per shape point count information (not created if LAYER has a geometry type of Point)
LAYER_part_node_count: used to store per part point count information (only created if LAYER consists of MultiLineStrings, MultiPolygons, or has at least one Polygon with interior rings)
LAYER_interior_ring: used to store interior ring information (only created if LAYER consists of at least one Polygon with interior rings)
LAYER_field_FIELD: used to store field information for FIELD.
These names are the only reserved names applying to CF-1.8 datasets.
CF-1.6/WKT datasets are not limited to the aforementioned restrictions.
Mapping of concepts
Field types
On creation of netCDF files, the mapping between OGR field types and netCDF type is the following :
OGR field type |
netCDF type |
|---|---|
String(1) |
char |
String |
char (bi-dimensional), or string for NC4 |
Integer |
int |
Integer(Boolean) |
byte |
Integer(Int16) |
short |
Integer64 |
int64 for NC4, or double for NC3 as a fallback |
Real |
double |
Real(Float32) |
float |
Date |
int (with units="days since 1970-1-1") |
DateTime |
double (with units="seconds since 1970-1-1 0:0:0") |
The driver also writes the following attributes for each OGR fields / netCDF variables.
ogr_field_name: OGR field name (useful if the netCDF variable name is different, due to collision)
ogr_field_type: OGR field type (such as String,Integer,Date,DateTime,etc...)
ogr_field_width: OGR field width. Only set if it is non-zero, except for strings
ogr_field_precision: OGR field precision. Only set if it is non-zero
standard_name": (GDAL >= 3.7) OGR field alternative name, if it is compatible of the convention for the value of standard_name.
long_name: (GDAL >= 3.7) OGR field comment, or if not set, OGR field alternative name if it is not compatible of standard_name, or "Field {ogr_field_name}" otherwise.
They are written by default (unless the WRITE_GDAL_TAGS dataset creation option is set to NO). They are not required for reading, but may help to better identify field characteristics
On reading, the mapping is the following :
netCDF type |
OGR field type |
|---|---|
byte |
Integer |
ubyte (NC4 only) |
Integer |
char (mono dimensional) |
String(1) |
char (bi dimensional) |
String |
string (NC4 only) |
String |
short |
Integer(Int16) |
ushort (NC4 only) |
Integer |
int |
Integer |
int or double (with units="days since 1970-1-1") |
Date |
uint (NC4 only) |
Integer64 |
int64 (NC4 only) |
Integer64 |
uint64 (NC4 only) |
Real |
float |
Real(Float32) |
double |
Real |
double (with units="seconds since 1970-1-1 0:0:0") |
DateTime |
Layers
In the CF-1.8 compliant driver, a single layer corresponds to a single
geometry container within a CF-1.8 compliant netCDF file. A geometry container, per
the CF-1.8 specification, is referred to by another variable
(presumably a data variable) through the geometry attribute. When reading
a CF-1.8 compliant netCDF file, all geometry containers within the netCDF file
will be present in the opened dataset as separate layers. Similarly, when writing to
a CF-1.8 dataset, each layer will be written to a geometry container whose variable
name is that of the source layer. When writing to a CF-1.8 dataset specifically, multiple layers are always
enabled and are always in a single netCDF file, regardless of the MULTIPLE_LAYERS
option.
When working with files made with older versions of the driver (pre CF-1.8),
a single netCDF file generally corresponds to a single OGR layer,
provided that it contains only mono-dimensional variables,
indexed by the same dimension (or bi-dimensional variables of type char).
For netCDF v4 files with multiple groups, each group may be seen as a separate OGR
layer. On writing, the MULTIPLE_LAYERS
dataset creation
option can be used to control whether multiple layers is disabled, or if
multiple layers should go in separate files, or separate groups.
Strings
Variable length strings are not natively supported in netCDF v3 format. To work around that, OGR uses bi-dimensional char variables, whose first dimension is the record dimension, and second dimension the maximum width of the string.
By default, OGR implements a "auto-grow" mode in writing, where the maximum width of the variable used to store a OGR string field is extended when needed.
For WKT datasets, this leads to a full rewrite of already written records; although this process is transparent for the user, it can slow down the creation process in non-linear ways. A similar mechanism is used to handle layers with geometry types other than point to store the ISO WKT representation of the geometries.
For CF-1.8 datasets, growing the string width dimension is a relatively inexpensive process which does not involve recopying of records, but involves only a simple integer reassignment. Because of how inexpensive dimension growth is with CF-1.8 datasets, auto growth of the string width dimension is always on.
When using a netCDF v4 output format (NC4), strings will be by default written as netCDF v4 variable length strings.
Geometry
Supported feature types when reading from a CF-1.8 convention compliant netCDF file include OGRPoint, OGRLineString, OGRPolygon, OGRMultiPoint, OGRMultiLineString, and OGRMultiPolygon. Due to slight ambiguities present in the CF-1.8 convention concerning Polygons versus MultiPolygons, the driver will in most cases default to assuming a MultiPolygon for the geometry of a layer with geometry_type polygon. The one exception where a Polygon type will be used is when the attribute part_node_count is not present within that layer's geometry container. Per convention requirements, the driver supports reading and writing from geometries with X, Y, and Z axes. Writing from source layers with features containing an M axis is also partially supported. The X, Y, and Z information of a measured feature will be able to be captured in a CF-1.8 netCDF file, but the measure information will be lost completely.
When working with a CF-1.6/WKT dataset, layers with a geometry type of Point or Point25D will cause the implicit creation of x,y(,z) variables for a projected coordinate system, or lon,lat(,z) variables for geographic coordinate systems. For other geometry types, a variable "ogc_wkt" ( bi-dimensional char for NC3 output, or string for NC4 output) is created and used to store the geometry as a ISO WKT string.
"Profile" feature type
The driver can handle "profile" feature type, i.e. phenomenons that happen at a few positions along a vertical line at a fixed horizontal position. In that representation, some variables are indexed by the profile, and others by the observation.
More precisely, the driver supports reading and writing profiles organized accordingly with the "Indexed ragged array representation" of profiles.
On reading, the driver will collect values of variables indexed by the profile dimension and expose them as long as variables indexed by the observation dimension, based on a variable such as "parentIndex" with an attribute "instance_dimension" pointing to the profile dimension.
On writing, the FEATURE_TYPE=PROFILE layer creation option must be set and the driver will need to be instructed which OGR fields are indexed either by the profile or by the observation dimension. The list of fields indexed by the profile can be specified with the PROFILE_VARIABLES layer creation options (other fields are assumed to be indexed by the observation dimension). Fields indexed by the profile are the horizontal geolocation (created implicitly), and other user attributes such as the location name, etc. Care should be taken into selecting which variables are indexed by the profile dimension: given 2 OGR features (taking into account only the variables indexed by the profile dimension), if they have different values for such variables, they will be considered to belong to different profiles.
In the below example, the station_name and time variables may be indexed by the profile dimension (the geometry is assumed to be also indexed by the profile dimension), since all records that have the same value for one of those variables have same values for the other ones, whereas temperature and Z should be indexed by the default dimension.
station_name |
time |
geometry |
temperature |
Z |
|---|---|---|---|---|
Paris |
2016-03-01T00:00:00Z |
POINT (2 49) |
25 |
100 |
Vancouver |
2016-04-01T12:00:00Z |
POINT (-123 49.25) |
5 |
100 |
Paris |
2016-03-01T00:00:00Z |
POINT (2 49) |
3 |
500 |
Vancouver |
2016-04-01T12:00:00Z |
POINT (-123 49.25) |
-15 |
500 |
An integer field, with the name of the profile dimension (whose default name is "profile", which can be altered with the PROFILE_DIM_NAME layer creation option), will be used to store the automatically computed id of profile sites (unless a integer OGR field with the same name exits).
The size of the profile dimension defaults to 100 for non-NC4 output format, and is extended automatically in case of additional profiles (with similar performance issues as growing strings). For NC4 output format, the profile dimension is of unlimited size by default.
Dataset creation options
GEOMETRY_ENCODING=[CF_1.8/WKT]: Defaults to
CF_1.8
. Chooses which geometry encoding to use when creating new layers within the dataset.FORMAT=[NC/NC2/NC4/NC4C]: Defaults to
Nc
. netCDF format. NC is the classic netCDF format (compatible of netCDF v3.X and 4.X libraries). NC2 is the extension of NC for files larger than 4 GB. NC4 is the netCDF v4 format, using a HDF5 container, offering new capabilities (new types, concept of groups, etc...) only available in netCDF v4 library. NC4C is a restriction of the NC4 format to the concepts supported by the classic netCDF format.WRITE_GDAL_TAGS=[YES/NO]: Defaults to
YES
.Whether to write GDAL specific information as netCDF attributes.
CONFIG_FILE=[<filename>/<xml>]: Path to a XML configuration file (or its content inlined) for precise control of the output.
The following option will only have effect when simultaneously specifying GEOMETRY_ENCODING=WKT
:
MULTIPLE_LAYERS=[NO/SEPARATE_FILES/SEPARATE_GROUPS]: Defaults to
NO
. Default is NO, i.e a dataset can contain only a single OGR layer. SEPARATE_FILES can be used to put the content of each OGR layer in a single netCDF file, in which case the name passed at dataset creation is used as the directory, and the layer name is used as the basename of the netCDF file. SEPARATE_GROUPS may be used whenFORMAT=NC4
to put each OGR layer in a separate netCDF group, inside the same file.
Layer creation options
The following option applies to both dataset types:
USE_STRING_IN_NC4=[YES/NO]: Whether to use NetCDF string type for strings in NC4 format. If NO, bidimensional char variable are used. Default to YES when
FORMAT=NC4
.
The following options require a dataset with GEOMETRY_ENCODING=WKT
:
RECORD_DIM_NAME=value: Defaults to
record
. Name of the unlimited dimension that index features.STRING_DEFAULT_WIDTH=<integer>: Default width of strings (when using bi-dimensional char variables). Default is 10 in autogrow mode, 80 otherwise.
WKT_DEFAULT_WIDTH=<integer>: Default width of WKT strings (when using bi-dimensional char variables). Default is 1000 in autogrow mode, 10000 otherwise.
AUTOGROW_STRINGS=[YES/NO]: Defaults to
YES
. Whether to auto-grow string fields of non-fixed width, or ogc_wkt special field, when serialized as bidimensional char variables. When set to NO, if the string is larger than its maximum initial width (set by STRING_DEFAULT_WIDTH), it is truncated. For a geometry, it is completely discarded.FEATURE_TYPE=[AUTO/POINT/PROFILE]: Defaults to
AUTO
. Select the CF FeatureType. Defaults to AUTO where FeatureType=Point is selected if the layer geometry type is Point, otherwise the custom approach involving the "ogc_wkt" field is used. Can be set to PROFILE so as to select the creation of an indexed ragged array representation of profiles.PROFILE_DIM_NAME=value: Defaults to
profile
. Name of the profile dimension and variable. Only used whenFEATURE_TYPE=PROFILE
.PROFILE_DIM_INIT_SIZE=value: Initial size of profile dimension, or UNLIMITED for NC4 files. Defaults to 100 when FORMAT != NC4 and to UNLIMITED when FORMAT = NC4. Only used when
FEATURE_TYPE=PROFILE
.PROFILE_VARIABLES=value: Comma separated list of field names that must be indexed by the profile dimension. Only used when
FEATURE_TYPE=PROFILE
.
The following option requires a dataset with GEOMETRY_ENCODING=CF_1.8:
BUFFER_SIZE=<bytes>: The soft limit of the write buffer in bytes. Larger values generally imply better performance, but values should be comfortably less than that of available physical memory or else thrashing can occur. By default, this value is set at 20% of usable physical memory (usable meaning total physical RAM considering limitations of virtual address space size). Buffer contents are committed between translating features, but not during translating a feature, so this limit does not apply to a single feature. The minimum acceptable size is 4096. If a value lower than this is specified the default will be used.
GROUPLESS_WRITE_BACK=[YES/NO]: Defaults to
NO
. In order to reduce time used to write data to the target netCDF file, data is often grouped together in arrays and written all at once. Each of these arrays is associated with a variable in the target dataset. Arrays are destroyed as soon as the associated data is written to the netCDF file which in turn occurs as soon as a complete data array for a variable is assembled in memory. For machines with small memory sizes, this optimization may cause issues when writing large datasets with large layers. Turning this option on by specifying "YES" disables array writing and causes data to be written one datum at a time. It is strongly recommended to keep this option off unless out of memory errors or performance issues occur. In the general case, this technique greatly improves translation efficiency.
XML configuration file
A XML configuration file conforming to the schema specified in netcdf_config.xsd can be used for very precise control on the output format, in particular to set all needed attributes (such as units) to conform to the NetCDF CF-1.6 convention.
It has been designed in particular, but not exclusively, to be usable in use cases involving the MapServer OGR output.
Such a file can be used to :
set dataset and layer creation options.
set global netCDF attributes.
map OGR field names to netCDF variable names.
set netCDF attributes attached to netCDF variables.
The scope of effect is either globally, when elements are defined as direct children of the root <Configuration> node, or specifically to a given layer, when defined as children of a <Layer> node.
The filename is specified with the CONFIG_FILE dataset creation option. Alternatively, the content of the file can be specified inline as the value of the option (it must then begin strictly with the "<Configuration" characters)
The following example shows all possibilities and precedence rules:
<Configuration>
<DatasetCreationOption name="FORMAT" value="NC4"/>
<DatasetCreationOption name="MULTIPLE_LAYERS" value="SEPARATE_GROUPS"/>
<LayerCreationOption name="RECORD_DIM_NAME" value="observation"/>
<!-- applies to all layers -->
<Attribute name="copyright" value="Copyright(C) 2016 Example"/>
<Field name="weight"> <!-- edit user field/variable -->
<Attribute name="units" value="kg"/>
<Attribute name="maximum" value="10" type="double"/>
</Field>
<Field netcdf_name="z"> <!-- edit predefined variable -->
<Attribute name="long_name" value="Elevation"/>
</Field>
<!-- start of layer specific definitions -->
<Layer name="1st_layer" netcdf_name="firstlayer"> <!-- OGR layer "1st_layer" is renamed as "firstlayer" netCDF group -->
<LayerCreationOption name="FEATURE_TYPE" value="POINT"/>
<Attribute name="copyright" value="Public domain"/> <!-- override global one -->
<Attribute name="description" value="This is my first layer"/> <!-- additional attribute -->
<Field name="1st_field" netcdf_name="firstfield"/> <!-- rename OGR field "1st_field" as the "firstfield" netCDF variable -->
<Field name="weight"/> <!-- cancel above global customization -->
<Field netcdf_name="lat"> <!-- edit predefined variable -->
<Attribute name="long_name" value=""/> <!-- remove predefined attribute -->
</Field>
</Layer>
<Layer name="sounding">
<LayerCreationOption name="FEATURE_TYPE" value="PROFILE"/>
<Field name="station_name" main_dim="profile"/> <!-- the corresponding netCDF variable will be indexed against the profile dimension, instead of the observation dimension -->
<Field name="time" main_dim="profile"/> <!-- the corresponding netCDF variable will be indexed against the profile dimension, instead of the observation dimension -->
</Layer>
</Configuration>
The effect on the output can be checked by running the ncdump utility
Further Reading
Credits
Development of the read/write vector capabilities for netCDF was funded by Meteorological Service of Canada , World Ozone and Ultraviolet Radiation Data Centre, and the US Geological Survey.


Epicodus Week 3 Code Review
Ping Pong will count up to your number from 1, with the following exceptions:


| Application title | Description | Fees |
|---|---|---|
| Update technical data | Update technical data | $4,500 |
| Permit for testing on public land | Permit for testing on public land | $4,400 |
| Addendum to Code 83.1a | Addendum to Code 83.1a | $4,300 |
| Request for evaluation | Request for evaluation | $4,200 |
| Request for change of ownership | Request for change of ownership | $4,100 |
| New development within private bandwidth | New development within private bandwidth | $4,000 |
| Permit for testing on public land | Permit for testing on public land | $3,900 |
| Addendum to Code 83.1a | Addendum to Code 83.1a | $3,800 |
| Request for evaluation | Request for evaluation | $3,700 |
| Request for change of ownership | Request for change of ownership | $3,600 |
UI Configuration
- Filter style
- Search results


Emoncms bug and dev list OCTOBER 2012
Thought I would try something new, keep a monthly bug and dev list here for emoncms. This will also be mirrored to the emoncms github issue tracker too.
If you can help with any of these that would be great.
If your interested in starting developing, you might want to start by looking at the application architecture page: http://openenergymonitor.org/emon/node/312 and also the pages here: http://openenergymonitor.org/emon/emoncms/development-challenges.
Bug list
Messages
Close X in blue twitter bootstrap type message box does nothing - the action is not yet coded.
Input processing
/ input - input process is not implemented correctly. It is at the moment implemented as a divide by value specified. It should be divide by the value of an input specified. http://openenergymonitor.org/emon/node/1064
strtotime() error on raspberry PI
http://openenergymonitor.org/emon/node/893
Add new dashboard "+" button does not function reliably across browsers
http://openenergymonitor.org/emon/node/945
There is no implementation of kwhd histogram zoomer in the visual dashboard editor
http://openenergymonitor.org/emon/node/1104
Dev list
New feeds implementation (in new modular emoncms version)
I have started a new feeds implementation that allows collapsible and expandable grouping, to complete and commit.
Text based status indicators
http://openenergymonitor.org/emon/node/296#comment-2486
View html source option in dashboard editor
Reinstating a feature that existed before
Import vs Export
https://github.com/openenergymonitor/emoncms3/issues/44
Modules
I think this is going to be a big advance for emoncms, to rearrange the directory structure into Modules i.e feed, input processing, user, dashboard, each with their own Controller, Model and Views. This should provide a surer footing for further development, expansion and code understandability. https://github.com/openenergymonitor/emoncms3/issues/47
See: github.com/emoncms/emoncms for new modular version, see blog post: Emoncms development update: Modules
Re: Emoncms bug and dev list OCTOBER 2012
Browser faults?
http://www.emoncms.org/mikerowland
Hi Trystan,
Internet Explorer 8: the dashboard designer doesn't work and the dials and cylinder are not visible - this is all I have at work so I can't keep an eye on my feeds...
Chrome: I haven't found a way to add the feed id's to Multigraph*the multigraph is configured on the API page - found it!*Overall though it's brilliant and I love it!
Regards,
Mike
Re: Emoncms bug and dev list OCTOBER 2012
Problem with page background.
Everytime i go back in edit, the page is smaller then the things i put on it.
Also when i viewe it the footer is out of place.
Re: Emoncms bug and dev list OCTOBER 2012
Did some digging for the IE8 and other "old" browsers that don't view the dials or graphics. Turned out that it's the behavior of the widget javascript (dial.js) that use a modern technology HTML-5 function called 'Canvas'. Canvas isn't supported (as well more HTML-5 tags) in older browsers than IE9 or old Firefox (and Chrome).
But there is a possible solution with a nice "side effect" to it. I bumped into a HTML5 feature detection tool called Modenizr. I'ts opensource off course (also on Github) and the site: http://modernizr.com/ has a feature to make a small custom javascript for the Canvas function.
I will try to get it work for the Canvas part with IE8. But I'm not a webdeveloper, and I don't have much CSS-stylesheet experience so I don't know If I will succeed with it
Re: Emoncms bug and dev list OCTOBER 2012
Hello Larsjo, fixed the dashboard page length issue in the new modular emoncms at github.com/emoncms. Created a page length property that is saved in the database so that the page length set the first time is ok the next time in edit mode. The modular emoncms is not fully complete yet so you may want to wait a little while before upgrading.
Thanks Alco, let me know how it goes.
Re: Emoncms bug and dev list OCTOBER 2012
Really like the latest update with vis and also grouping of nodes.
Anyhow it looks like a dummy node_0 was created mixing up the feed id:s. Node_0 is at the end of the list.
Re: Emoncms bug and dev list OCTOBER 2012
Thanks Petrik, what do you mean by dummy node_0 was created mixing up the feed id:s?
Created a new bug and dev list for november here, lets carry on there: http://openenergymonitor.org/emon/node/1299


Suinova Comics provides a gallery game for readers to collect virtual items from the books. Gallery Credits can be earned through daily visits, and used to expand the gallery space. One gallery room can be decorated for a particular story scene in a book or with mixed items from different stories. It is totally controled by the readers.
- What's the gallery for?
- Every reader has a free virtual gallery with limited space/rooms
- The gallery is used to display collected items (decoration)
- It is a show room to let your friends see your collections
- What can be placed in the gallery?
- Items to be placed in your gallery are collected from the books you read
- They can be virtual goods you bought or given as rewards
- Or exchanged with friends if possible
- The items can be a flower, a weapon, a character, or a background picture
- How to add items to the gallery?
- Go to the Readers page (Readers button above)
- Check your Inventory below the gallery space
- Click Add button below the item you want to display
- Wait and see the item sent to your gallery room
- How do I manipulate the items?
- You can move an item around in the same room
- Resize an item (click it, drag the lower-right corner)
- Put it above or below all others (click, up arrow for above, down for below)
- Remove an item from the gallery and return it to the inventory
- How to expand the space?
- As you read more books and collect more items, your gallery is too crowded
- You can expand your space by adding more gallery rooms
- You can buy additional rooms or level up with Gallery Credits
- Gallery credits are increased with daily visits
- How to increase my Gallery Credits?
- Login to your gallery every day to get a free credit
- Send an invitation ticket to 5 friends daily
- And you'll get a free credit if a friend visits your gallery


Click here to trigger the animation (and add a state hash).
Click here to reset the animation and the URL.
This example created for the "Good, Better, Best" AQB page.
See the commented source for full details.
Place the content to be animated into a page fragment, and use that fragment in a standalone page (for when JavaScript is disabled), as well as in this page.
When the trigger link is clicked, we:
We also check for a hash value on page load, triggering the appropriate behavior when appropriate.
We are using Really Simple History (RSH) to manage page navigation (back, forward) and bookmarking in coordination with dynamic page states (Ajax, DHTML).
Tested on Windows: Firefox 3, IE7, IE6, Safari 3, Opera 9, Chrome 1; and Mac OS X: Firefox 2.
The older version of effects.js used in ABQ seems to have an issue with Effect.Grow in IE8. Similarly, IE5.5 has issues, as well.
(Note that MyNationwide.js
shows errors
beginning at line 288 (isRequired
).


DbFiles implementation
Dbfiles is a library, which provides the service to store files in the database.
It uses two database tables, files and files_data. The table files is used to store all
the information about the files except the content of the files.
The files_data table stores the content of the files as variable length bytea.
There are two operation modes, dbfiles can do management of folders or management
of folders can be done by the App. This interface makes use of tasks.h.
This means all requests start tasks and the UTask callbacks are used to indicate completion.
To create an IDbFiles instance, at first an instance of IDbFilesProvider must be created. This is done by calling CreateDbFilesProvider.
File information
Logging
To enable logging, the flag LOG_DBFILES must be set in the managers diagnostic settings.
Functions
Functions to initialize
extern "C" IDbFilesProvider * CreateDbFilesProvider();
Overview
This function will create an IDbFilesProvider object which can be used to create IDbFiles instances.
CreateDbFilesProvider
-
Return value
The IDbFilesProvider instance. This instance can be freed as soon as it is no longer used by calling the C++ delete operator.
Classes
IDbFilesProvider
class IDbFilesProvider {
public:
class IDbFiles * CreateDbFiles(class IIoMux * iomux, class IInstanceLog * const log);
};
Public functions
CreateDbFiles
-
This function creates a new IDbFiles object which can be used to create tasks for writing, reading, ... of files.
Parameters
| class IIoMux * const iomux | The IIoMux instance. |
| class IInstanceLog * const log | The IInstanceLog instance used for loging purposes. |
IDbFiles
class IDbFiles {
public:
class IDbFilesStart * Start(class UTask * user, class IDatabase * database, const char * folder);
class IDbFilesPut * Put(class UTask * user, const char * name, ulong64 folder, bool is_folder = false);
class IDbFilesGet * Get(class UTask * user, ulong64 id, unsigned offset = 0, bool progress = false);
class IDbFilesDel * Del(class UTask * user, ulong64 id);
class IDbFilesMove * Move(class UTask * user, ulong64 id, const char * name, ulong64 folder);
class IDbFilesList * List(class UTask * user, ulong64 folder, ulong64 limit);
class IDbFilesPathInfo * PathInfo(class UTask * user, const char * path);
};
Public functions
Start
-
Creates an IDbFilesStart instance to start the database initialization for dbfiles. Must be called when the database initialization is complete.
Parameters
| class UTask * user | UTask user for the callbacks for task completion. |
| class IDatabase * database | The IDatabase instance of the database connection. |
| const char * folder |
A foreign key definition if the App handles folder management itself
(e.g. "BIGINT REFERENCES messages(id) ON DELETE CASCADE"). A value of null indicates that dbfiles
should do folder management.
It is important to use the ON DELETE CASCADE functionality of the foreign key, otherwise
the files/folders can't be deleted correctly!
|
Return value
An instance of class IDbFilesStart.
Remarks
If dbfiles does the folder management, the root folder alway uses the id 1 (DBFILES_ROOT).
Put
-
Creates an IDbFilesPut instance to put a new file into the database.
Parameters
| class UTask * user | UTask user for the callbacks for task completion. |
| const char * name | Name of the file. If dbfiles does folder management the name, folder combination must be unique, otherwise there are no constraints for the file name. |
| ulong64 folder | Reference in which folder this file is to be put. |
| bool is_folder | Only for folder management. If true a folder is put, not a file. |
Return value
An instance of class IDbFilesPut.
Get
-
Creates an IDbFilesGet instance to get a file from the database.
Parameters
| class UTask * user | UTask user for the callbacks for task completion. |
| ulong64 id | id of the file to get (can be retrieved with IDbFilesList or IDbFilesPathInfo). |
| unsigned int offset | Offset inside the file at which to start reading. |
| bool progress | The flag progress is used to indicate that TaskProgress shall be called in any case when the data is available, even if this is the last block of data. In this case TaskComplete is called after the next Read() call, when the file is closed.. |
Return value
An instance of class IDbFilesGet.
Del
-
Creates an IDbFilesDel instance to get a file from the database.
Parameters
| class UTask * user | UTask user for the callbacks for task completion. |
| ulong64 id | id of the file to delete (can be retrieved with IDbFilesList or IDbFilesPathInfo). |
Return value
An instance of class IDbFilesDel.
Move
-
Creates an IDbFilesMove instance to move a file or folder.
Parameters
| class UTask * user | UTask user for the callbacks for task completion. |
| ulong64 id | id of the file or folder to move (can be retrieved with IDbFilesList or IDbFilesPathInfo). |
| const char * name | The new name of the file or folder. |
| ulong64 newFolder | id of the new folder to move to (can be retrieved with IDbFilesList or IDbFilesPathInfo). |
Return value
An instance of class IDbFilesMove.
List
-
Creates an IDbFilesList instance to list files and folders inside a folder.
Parameters
| class UTask * user | UTask user for the callbacks for task completion. |
| ulong64 folder | id of the folder to list (can be retrieved IDbFilesPathInfo). |
| ulong64 limit | Number of entries to get. If a limit is set (limit!=0), all the files of the folder are read into memory. |
Return value
An instance of class IDbFilesList.
PathInfo
-
Creates an IDbFilesPathInfo instance to get information about a file or folder.
Parameters
| class UTask * user | UTask user for the callbacks for task completion. |
| const char * path | File path to be tested. Should not start with '/'. |
Return value
An instance of class IDbFilesPathInfo.
IDbFilesStart
Starts the database initialization for dbfiles. Must be called when the database initialization is complete. The ITask Start() method is implicitly called!
class IDbFilesStart {
public:
};
IDbFilesGet
Gets a file from the database. The ITask Start() method is implicitly called!
After TaskProgress (or TaskFinished) Get can be called to get the read data.
With Read the next data can be requested, which will be indicated with TaskProgress again. If no more data is available
TaskComplete is called.
TaskFailed is called on errors.
class IDbFilesGet {
public:
void Get(const byte * & data, unsigned & length);
void Read(bool last=false);
void Stop();
};
Public functions
Get
-
Get the read data. The data pointer is set to a pointer allocated by dbfiles and
length is set to the length of the data. Do not free data, as data is handled by IDbFilesGet!
Parameters
| const byte * & data | Will be filled with available data. |
| unsigned & length | Will be set to the length of the available data. |
Read
-
Initiate next read.
Parameters
| bool last | last is used to indicate that this is the last data to be read even if there is more data available. |
Stop
-
Stops the current operations which will trigger an asynchronous TaskComplete/TaskFailed.
IDbFilesPut
Puts a new file into the database. The ITask Start() method is implicitly called!
After the TaskComplete or TaskProgress callbacks, the function call Get can be used to read the id of the new file.
After TaskProgress the function Write can be called to write data to the file. This creates a new row in the files_data table.
TaskFailed is called on errors.
class IDbFilesPut {
public:
ulong64 Get();
void Write(const byte * data, unsigned length, bool last);
const char * GetName();
void Stop();
};
Public functions
Get
-
Return value
The id of the file.
Remarks
Can be first called after TaskProgress/TaskComplete.
Write
-
Can be called after TaskProgress to write data to the file. This creates a new row in the files_data table.
Parameters
| const byte * data | The data to write. |
| unsigned length | The length of the data to write. |
| bool last | last is used to indicate that this is the last data to write, so TaskComplete is called afterwards. |
Stop
-
Stops the current operation which will trigger an asynchronous TaskComplete/TaskFailed.
IDbFilesDel
Deletes a file from the database. The ITask Start() method is implicitly called!
TaskFailed is called on errors.
class IDbFilesDel {
public:
};
IDbFilesMove
Moves a file inside the database. The ITask Start() method is implicitly called!
TaskFailed is called on errors.
class IDbFilesMove {
public:
};
IDbFilesList
Gets the list of files assigned to a given folder. The ITask Start() method is implicitly called!
If a limit is set, TaskComplete indicates that the list is availabe. The entries in the list can be read with multiple calls to Get until true is returned.
If no limit is set the entries are read one by one. TaskProgress indicates that an entry is available, which can be read with Get.
With Next the next entry can be requested. If there are no more entries available TaskComplete is called.
class IDbFilesList {
public:
bool Get(ulong64 & id, const char * & name, unsigned & length, bool & is_folder, ulong64 * created = 0, ulong64 * modified = 0);
void Next();
void Stop();
};
Public functions
Get
-
Parameters
| ulong64 & id | The id of the file or folder. |
| const char * & name | |
| unsigned & length | The file size. |
| bool & is_folder | true if it's a folder. |
| ulong64 * created | Creation time. |
| ulong64 * modified | Last modification time. |
Return value
true indicates that this was the last file in the list.
Next
-
Starts reading the next item in the list.
Stop
-
Stops the current operation which will trigger an asynchronous TaskComplete/TaskFailed.
IDbFilesPathInfo
Gets information of a file or folder.
class IDbFilesList {
public:
void Get(ulong64 & id, const char * & name, unsigned & length, bool & is_folder, ulong64 * created = 0, ulong64 * modified = 0);
void GetFailed(ulong64 & id, bool & is_folder, const char * & path);
};
Public functions
Get
-
Function which can be called from TaskComplete, to get the result.
Parameters
| ulong64 & id | The id of the file or folder. |
| const char * & name | |
| unsigned & length | The file size. |
| bool & is_folder | true if it's a folder. |
| ulong64 * created | Creation time. |
| ulong64 * modified | Last modification time. |
GetFailed
-
Function which can be called from TaskFailed, to get the result.
Parameters
| ulong64 & id | The id of the last part of the path, which was found. |
| bool & is_folder | true if it's a folder. |
| const char * & path | The path as it was found. |


| 1 |
Barr_2008 |
Pandemic influenza in Australia: Using telephone surveys to measure perceptions of threat and willingness to comply |
Barr, Margo; Raphael, Beverley; Taylor, Melanie; Stevens, Garry; Jorm, Louisa; Giffin, Michael; Lujic, Sanja |
2008 |
2008-09-15 |
COMM-USE |
N |
PMC2556339 |
18793441.0 |
10.1186/1471-2334-8-117 |
ywb9krdp |
0.807820 |
|
Hickey_2014, Lam_K_2013 |
| 2 |
Paterson_2014 |
Cross sectional survey of human-bat interaction in Australia: public health implications |
Paterson, Beverley J; Butler, Michelle T; Eastwood, Keith; Cashman, Patrick M; Jones, Alison; Durrheim, David N |
2014 |
2014-01-21 |
COMM-USE |
N |
PMC3908316 |
24443960.0 |
10.1186/1471-2458-14-58 |
w054dkn1 |
0.736438 |
Wang_2014 |
|
| 3 |
Wu_P_2015 |
Live Poultry Exposure and Public Response to Influenza A(H7N9) in Urban and Rural China during Two Epidemic Waves in 2013-2014 |
Wu, Peng; Wang, Liping; Cowling, Benjamin J.; Yu, Jianxing; Fang, Vicky J.; Li, Fu; Zeng, Lingjia; Wu, Joseph T.; Li, Zhongjie; Leung, Gabriel M.; Yu, Hongjie |
2015 |
2015-09-14 |
COMM-USE |
N |
PMC4569561 |
26367002.0 |
10.1371/journal.pone.0137831 |
8kaim4z1 |
0.656772 |
|
|
| 4 |
Taylor_2009 |
Public health measures during an anticipated influenza pandemic: Factors influencing willingness to comply |
Taylor, Melanie; Raphael, Beverley; Barr, Margo; Agho, Kingsley; Stevens, Garry; Jorm, Louisa |
2009 |
2009-01-29 |
PMC |
N |
PMC3270909 |
22312204.0 |
|
e94synjc |
0.639059 |
Knapp_2008, Della_Polla_2020 |
Kok_G_2010, Hickey_2014 |
| 5 |
Jacobs_2010 |
Factors Associated with Increased Risk Perception of Pandemic Influenza in Australia |
Jacobs, Jennifer; Taylor, Melanie; Agho, Kingsley; Stevens, Garry; Barr, Margo; Raphael, Beverley |
2010 |
2010-07-20 |
COMM-USE |
N |
PMC3447299 |
23074650.0 |
10.1155/2010/947906 |
kmdxxx76 |
0.637652 |
|
Hickey_2014 |
| 6 |
Wang_2014 |
Human Exposure to Live Poultry and Psychological and Behavioral Responses to Influenza A(H7N9), China |
Wang, Liping; Cowling, Benjamin J.; Wu, Peng; Yu, Jianxing; Li, Fu; Zeng, Lingjia; Wu, Joseph T.; Li, Zhongjie; Leung, Gabriel M.; Yu, Hongjie |
2014 |
2014-08-23 |
PMC |
N |
PMC4111172 |
25076186.0 |
10.3201/eid2008.131821 |
j3y7sy0k |
0.621576 |
Ly_S_2007 |
Wu_P_2015 |
| 7 |
Beaumont_2007 |
A survey of the preparedness for an influenza pandemic of general practitioners in the West Midlands, UK |
Beaumont, M.; Duggal, H. V.; Mahmood, H.; Olowokure, B. |
2007 |
2007-08-10 |
PMC |
N |
PMC7088097 |
17690927.0 |
10.1007/s10096-007-0377-2 |
dtua8dvb |
0.604655 |
DeWitt_2020, Knapp_2008 |
Hickey_2014, Lam_K_2013 |
| 8 |
Wu_P_2014 |
Responses to Threat of Influenza A(H7N9) and Support for Live Poultry Markets, Hong Kong, 2013 |
Wu, Peng; Fang, Vicky J.; Liao, Qiuyan; Ng, Diane M.W.; Wu, Joseph T.; Leung, Gabriel M.; Fielding, Richard; Cowling, Benjamin J. |
2014 |
2014-05-23 |
PMC |
N |
PMC4012820 |
24750988.0 |
10.3201/eid2005.131859 |
hmf80ipo |
0.549043 |
Ly_S_2007 |
|
| 9 |
Lee_M_2015 |
Handwashing with soap and national handwashing projects in Korea: focus on the National Handwashing Survey, 2006-2014 |
Lee, Moo-Sik; Hong, Su Jin; Kim, Young-Taek |
2015 |
2015-08-31 |
COMM-USE |
N |
PMC4652062 |
26725224.0 |
10.4178/epih/e2015039 |
5gwnyn8z |
0.501264 |
Knapp_2008 |
Wong_2019, Wong_2005 |
| 10 |
Samsudin_2020 |
Awareness, Knowledge, Attitude and Preventive Practice of Leptospirosis Among Healthy Malaysian and Non-Malaysian Wet Market Workers in Selected Urban Areas in Selangor, Malaysia |
Samsudin, Suhailah; Saudi, Sakinah N.S.; Masri, Norbaya S.; Ithnin, Nur Raihana; T.Z.M.T, Jamaluddin; Hamat, Rukman A.; Wan Mohd, Zahiruddin W.M.; Nazri, Mohd S.; Surianti, Sukeri; Daud, Aziah B.; Abdullah, Munirah N.; Noramira, Nozmi; Osman, Malina |
2020 |
2020-02-19 |
COMM-USE |
N |
PMC7068448 |
32093098.0 |
10.3390/ijerph17041346 |
ybrjh9pe |
0.489881 |
Knapp_2008 |
Osbjer_2015 |
| 11 |
Huynh_2020 |
The more I fear about COVID-19, the more I wear medical masks: A survey on risk perception and medical masks uses |
Toan D Huynh |
2020 |
2020-03-30 |
BioRxiv |
Y |
|
|
10.1101/2<IP_ADDRESS>44388 |
tfspedf1 |
0.449256 |
|
|
| 12 |
Chuang_2015 |
Social Capital and Health-Protective Behavior Intentions in an Influenza Pandemic |
Chuang, Ying-Chih; Huang, Ya-Li; Tseng, Kuo-Chien; Yen, Chia-Hsin; Yang, Lin-hui |
2015 |
2015-04-15 |
COMM-USE |
N |
PMC4398366 |
25874625.0 |
10.1371/journal.pone.0122970 |
2e1zd80i |
0.433222 |
|
Timpka_2014, Hickey_2014, Wong_2010 |
| 13 |
Ho_H_2012 |
Use of face masks in a primary care outpatient setting in Hong Kong: Knowledge, attitudes and practices |
Ho, H.S.W. |
2012 |
2012-12-31 |
PMC |
N |
PMC7111693 |
23153561.0 |
10.1016/j.puhe.2012.09.010 |
jxnd7r9u |
0.426012 |
Knapp_2008 |
Cruz_2016, Nichol_2013 |
| 14 |
Zhou_2016 |
Fear and trust: How risk perceptions of avian influenza affect Chinese consumers’ demand for chicken |
Zhou, Li; Turvey, Calum G.; Hu, Wuyang; Ying, Ruiyao |
2016 |
2016-09-30 |
PMC |
N |
PMC7148954 |
|
10.1016/j.chieco.2016.06.003 |
rk7ijexa |
0.421959 |
|
|
| 15 |
Li_T_2014 |
Attitudes, practices and information needs regarding novel influenza A (H7N9) among employees of food production and operation in Guangzhou, Southern China: a cross-sectional study |
Li, Tiegang; Feng, Jing; Qing, Pengzhe; Fan, Xiaomei; Liu, Weisi; Li, MeiXia; Wang, Ming |
2014 |
2014-01-02 |
COMM-USE |
N |
PMC3899619 |
24383626.0 |
10.1186/1471-2334-14-4 |
s1gnwb2s |
0.421622 |
Della_Polla_2020 |
Chan_2015 |
| 16 |
Lau_J_2008 |
Perceptions Related to Bird-to-Human Avian Influenza, Influenza Vaccination, and Use of Face Mask |
Lau, J. T. F.; Kim, J. H.; Tsui, H. Y.; Griffiths, S. |
2008 |
2008-09-15 |
PMC |
N |
PMC7099207 |
18795229.0 |
10.1007/s15010-008-7277-y |
jxonslhh |
0.416268 |
|
Tam_D_2007 |
| 17 |
Atchison_2020 |
Perceptions and behavioural responses of the general public during the COVID-19 pandemic: A cross-sectional survey of UK Adults |
Christina J Atchison; Leigh Bowman; Charlotte Vrinten; Rozlyn Redd; Philippa Pristera; Jeffrey W Eaton; Helen Ward |
2020 |
2020-04-03 |
BioRxiv |
Y |
|
|
10.1101/2<IP_ADDRESS>50039 |
j7hih6o9 |
0.323987 |
Knapp_2008, Reyes-Velázquez_2015, Della_Polla_2020 |
Timpka_2014, Hickey_2014 |
| 18 |
Ly_S_2007 |
Interaction Between Humans and Poultry, Rural Cambodia |
Ly, Sowath; Van Kerkhove, Maria D.; Holl, Davun; Froehlich, Yves; Vong, Sirenda |
2007 |
2007-01-23 |
PMC |
N |
PMC2725837 |
17370527.0 |
10.3201/eid1301.061014 |
4psg0z3n |
0.319690 |
|
|
| 19 |
Blake_2010 |
Employment and Compliance with Pandemic Influenza Mitigation Recommendations |
Blake, Kelly D.; Blendon, Robert J.; Viswanath, Kasisomayajula |
2010 |
2010-02-23 |
PMC |
N |
PMC2958001 |
20113549.0 |
10.3201/eid1602.090638 |
4q04teum |
0.319390 |
|
Bellia_2013 |


MYSTERY SOLVED!
Scientists Reveal 20 Most Surprising
Superfoods for Amazing Health
Eggs, fish oil, butter, coconut oil . . . one minute they’re good for you and the next they’re not. Food fads make it tough to know what’s truly healthy. Eat it, don’t eat it. It helps, no wait, it doesn’t help.
Medical doctors and researchers are constantly making new discoveries about food that change conventional thinking.
With so much conflicting science, how do you know which foods really have incredible healing powers?
End the confusion once and for all
with The Healing Powers of Superfoods.
Written by Cal Orey, author of the bestselling Healing Powers of Honey, this blockbuster new book is based on the latest scientific studies showing dozens of surprising superfoods and juices you never knew could help you get healthy and stay healthy — even cheese, chocolate, and ice cream!
The Healing Powers of Superfoods reveals the NEW top 20 superfoods which are loaded with antioxidants, polyphenols, carotenoids, flavonoids, and minerals and vitamins with powerful immune-boosting and disease-preventing properties.
If you want to outsmart heart disease, cognitive
decline, or even lung troubles without giving up
your favorite foods . . . this is the book for you.
Contrary to popular belief, superfoods aren’t just for health nuts. They’re for everyone who wants to enjoy a heathier heart, a stronger brain, clear lungs and sinuses, lower cholesterol, and reduced pain — as well as give the boot to colds and flu, soothe dry skin, feel more energized, and live longer.
One of the most important things you can do with the NEW Top 20 superfoods: Boost your immunity. A balanced immune system is essential to living a healthy life.
Inside this 316-page bible of superfoods you’ll discover the top 20 nutrient-dense foods that are delicious to eat and easy to find at your favorite supermarket. These new superfoods are many of the classic foods you love and they deliver big health benefits that can help you:
- Boost your immune system
- Support a healthy heart
- Build strong bones
- Strengthen your cognitive health
- Maintain healthy blood sugar
- Power up your energy
- Fight fat
- Lower blood pressure
- Lower the risk of heart disease
- Slow the aging process
- Promote healthy vision
- Ease anxiety and stress
If foods like acai, maca, or garlic come to mind when you hear the word “superfood,” you’re in for a surprise! While these superfoods are nutritionally dense and good for your health . . .
The Secret to a Long, Healthy Life
Is Sitting in Your Grocery Shelf
There are loads of other, lesser-known taste-tempting, belly-filling foods with the power to restore health, wellness, and energy to the body. There are even foods that have been on your plate all along — you just didn’t know they were nutrient-dense. Take a look here in your FREE COPY of The Healing Powers of Superfoods.
Take gelato and shellfish — once considered too fatty or too high in cholesterol and sodium, they ve been determined to be good for you and they taste great. They’re today’s “new” superfoods! Shrimp alone have 17 grams of protein and almost one-third the recommended daily allowance of vitamin D — an important vitamin millions of people are lacking. You need all the “D” you can get for strong bones and teeth.
 Surprise — they’re immunity-boosting powerhouses!
Not just fat-free and a great source of potassium for your heart health, these super spuds are also packed with vitamin A, a powerful immune booster. Medical researchers also believe this potato can boost white blood cell activity, which is key to warding off certain cancers.
All of the details you need about super taters — including some great ways to use them in recipes — are on page 104 of your FREE BOOK. Get your FREE copy of The Healing Powers of Superfoods before they’re all gone!
Guess what? A food doesn’t have to be
perfectly “clean” to be a superfood.
Think about how you enjoy that big bowl of decadent gelato. So creamy and smooth, yet this sweet treat has less fat and more natural ingredients than ice cream.
Add some of this yummy chocolate or any of the great mix-in suggestions on page 63 and it’s like you went to Dairy Queen for a Blizzard swirled with your favorite mix-ins — only better because it’s healthier!
Best of all when you start adding other nutrient-dense foods to superfoods, you instantly increase the health benefits. The Healing Powers of Superfoods gives you all kinds of ideas for blending and stacking superfoods for the healthiest options.
Now don’t give ice cream the boot, because
America’s favorite frozen delight not only builds
strong bones — it supercharges your brain.
Ice cream contains calcium for bone-building and protein for strong muscles and teeth. Know what else? Vanilla ice cream is good for the brain! When you get your FREE COPY of The Healing Powers of Superfoods, turn to page 63 to see the secret super mineral in vanilla ice cream that enhances brain function. Belgian researchers using MRI testing found ice cream even diminishes the brain’s response to sadness.
Do you like apples or peanut butter? They’re super delicious superfoods, too. Apples aren’t just great for weight loss, they lower your bad cholesterol (LDL), protecting against heart attack and stroke.
See page 28 of your FREE BOOK to see which apples pack the most powerful punch. Be sure to try the Super Heart Healthy Cinna-Apple Smoothie on page 29.
Do these look like SUPERFOODS to you?
Take a closer look.
When University of Scranton researcher Joe Vinson, Ph.D., was asked what foods he’d pick if he were stranded on a desert island for two weeks, he chose nuts, peanut butter, orange juice, and wine. Since wine is rich in resveratrol, it could help prevent blood clots from years of eating French fries and bacon.
Rather skip the wine? There’s a great juice alternative on page 141. This superjuice not only helps lower cholesterol but may reduce the risk of cancer, too. It’s all in The Healing Powers of Superfoods. Claim your FREE copy here.
Eggs can keep you out of the cardiologist’s office, too.
Two studies of 40,000 men and women published in the Journal of the American Medical Association (JAMA) showed eating eggs in moderation may be good for your heart. Scientists found absolutely NO LINK to heart disease in healthy test subjects who ate an egg every day. But . . .
You must know which part of the egg to eat for the biggest health benefit — and it’s not what you think or what you’ve been told. An “eggstrordinary” secret on page 55 will show you how to get the biggest bang for your heart health from eggs.
Surprising superfood of the year:
Lobster Mac and Cheese for weight loss!
You’re going to think, “This is too decadent to be good for me, right?” Nope! This amazing recipe uses a secret type of pasta studies show fills you up and slims you down. Parmesan swirled with a bit of butter, pasta, tomatoes, creamy half-and-half, fresh basil. Mmm, mouthwatering! Get the recipe on page 286 of your FREE BOOK! Hurry, get your FREE COPY while supplies last.
These superfoods could help halt everything from heart disease, cancer, and flu to obesity, anxiety, and depression.
Power up immune system with this surprising breakfast food
Turn to page 71 of your FREE BOOK to find out!
Eat pizza, beat heart disease.
See how on page 91
Lower bad cholesterol with super chicken!
Turkey works, too. Try the moist and meaty Super Vitalizing Turkey Salad on page 100.
Exploding with turkey, pineapple, cashews, and onion, it’s a delicious feast for lunch or dinner.
Breathe easier — eat tomatoes.
Got asthma? Tomatoes open up airways and detox the body. Page 213
Crunchy French Toast and Berries
Wait until you taste this divine creation of cinnamon swirled with vanilla, maple syrup, butter, and berries.
Healthy never looked or tasted so good! Recipe on page 151 of your FREE BOOK.
What are you waiting for? Click here to get your FREE BOOK now! During flu season or a major health crisis, it’s more critical than ever you feed your body superfoods to shield against invaders. The more superfoods you pile on your plate over the course of a day, the better.
And learning about which superfoods to eat doesn’t have to be difficult. Everything you need to know is inside the NEW Healing Powers of Superfoods. Through this special offer, you can get a copy of this blockbuster new book absolutely FREE! Click here to claim your FREE BOOK.
Here’s a sneak peek at what’s waiting for you inside your FREE COPY of The Healing Powers of Superfoods:
- Lower your risk of cancer and heart disease with pizza! Italian researchers found the secret in the sauce. Page 92
- See cholesterol levels you can love at your next checkup. Eat more of this family favorite which also helps you stay trim and feel calm. Page 97
- Lower your blood pressure, stave off colds and flu, AND lose weight with . . . potatoes? That’s right! Discover which 2 “super spuds” are best on page 104.
- Reduce the risk of heart disease and power up your brain health with sweet, creamy, and crunchy Peanut Butter Fudge. Recipes on page 248
- Flatten your belly and banish bloat fast with watermelon — a natural diuretic! Try the quick Melon Salad on page 135.
- Tired, fatigued, and need energy? Eat this once-forbidden sweet treat turned superfood — it gives you the added benefit of boosting brain power. Page 63
- Need a sweetener? Forget honey or brown sugar — reach for antioxidant-rich maple syrup! Studies show maple can enhance the immune system for fewer colds and flu. WAIT! You must use THIS maple syrup to reap the health benefits. Page 73
- Avoid dental bills — keep your teeth and gums healthy with this vitamin C-packed fruit. Hint: it’s NOT oranges! Page 159
- Calm anxiety and frayed nerves with this Thanksgiving favorite. It helps your body make mood-boosting serotonin. Page 97
- Bounce back from jet lag! Drink a glass of water infused with this citrus and eat a handful of this superfood. You get a big energy boost from the carbs combined with magnesium and selenium. Page 198.
The Healing Powers of Superfoods is packed cover-to-cover with hundreds of ways to incorporate superfoods into your life to help you address whatever health issues you face: depression, dizziness, insomnia, kidney stones, blisters, UTIs, asthma, anemia, allergies, pain, and many others.
You get 50+ delicious, nutritious recipes for everything from Pesto Pizza, Dark Chocolate Gelato, and Cheesy Quiche to ricotta-rich Cannelloni steeped in garlic and herbs and Super Immunity Enhancing Chilled Sweet Potato Soup with Maple-Smoked Chili Sauce on page 105.
There are even thirst-quenching recipes for drinks and smoothies. Be sure to try the new twist on green drinks which tastes great! You can’t go wrong with sweet Honeycrisp apples blended with cinnamon, honey, and a touch of kale. Page 188
PLUS, this treasure trove of healing
superfoods book also contains:
Lower your blood pressure — say CHEESE, please!
All this time you thought cheese was a no-no. Cheese is loaded with heart healthy calcium to help lower your blood pressure — and your risk of heart disease and stroke. Page 41
Wait until you try the gooey Cheese and Cranberry Dip on page 35.
A sensational blend of melted Brie, wine, garlic, and cranberries tempts your taste buds.
- 50 home remedies you can zip together in your kitchen for everything from building strong immunity to dealing with gingivitis, heartburn, depression, constipation, kidney stones, infection, and other health concerns.
- Top 10 “skinny superfoods” scientifically shown to fill you up, not out!
- 5 simple rules for losing weight like a champ.
- Perfectly pampering beautifying recipes for shiny, healthy hair, glowing skin, soft elbows and feet, and more. Super Beautifying Cantaloupe Smoothie, anyone? See page 224
- Household helpers for cleaning stubborn rust, removing gum from carpets, and making your house smell fresh and clean.
 The Healing Powers of Superfoods is for you!
This groundbreaking new book is also perfect for anyone who wants to benefit from healthier options in their life, whether cooking, cleaning, dieting, or beautifying. There’s something for everyone!
Claim your FREE COPY today. All we ask is that you pick up the small shipping and handling fee of $4.95. Wait, there are two more things you’re going to love . . .
Meet Dr. Crandall
Chauncey W. Crandall I.V., M.D., F.A.C.C., is a renowned cardiologist in the U.S., and the leader of medical missions across the world for 40+ years. He is chief of the Cardiac Transplant Program at Florida’s Palm Beach Cardiovascular Clinic, a New York Times bestselling author and edits the popular newsletter, Dr. Crandall’s Heart Health Report. He practices interventional, vascular, and transplant cardiology, leading medical teams on the front lines of war against the plagues that devastate poor communities throughout the world.
He received his post-graduate medical training at Yale University School of Medicine and did a cardiology fellowship at Beth Israel Hospital and Mount Sinai Medical Center in New York City. Upon relocating to Palm Beach in 1993, he established the Duke University Cardiology Program affiliated with the cardiology division of Good Samaritan Hospital in that area.
Dr. Crandall has conducted a number of research studies and clinical trials and published his research in several prestigious medical journals.
He is married and the father of two sons. He is also the board chairman and Founder of The Chadwick Foundation, a nonprofit dedicated to building clinics and orphanages around the world with a message of love and hope. He established the foundation in memory of his late son.
Descended from seven generations of ministers, he now ministers to his patients through his expertise as a cardiologist and prays for their healing through the power of God.
We teamed up with esteemed Yale and Harvard University-trained cardiologist Dr. Chauncey Crandall to give you the latest heart health insights.
Famed cardiologist Dr. Chauncey Crandall believes that a great diet incorporating the right foods is critical for optimal heart health.
As chief of the Cardiac Transplant Program at Florida’s renowned Palm Beach Cardiovascular Clinic, Dr. Crandall is dedicated to minimizing poor outcomes from heart disorders.
Being on the frontlines of heart disease every day, he’s dedicated his life to spreading the word about prevention methods as well as the critical importance of early diagnosis.
Dr. Crandall also understands the lifesaving role food plays in keeping a heart healthy. Knowing which foods are best for your body is a crucial first step in protecting your heart. That’s why we’re offering you . . .
The Healing Powers of Superfoods FREE With This Special Offer!
(only pay $4.95 S/H)
Because cardiovascular health is so critical to your everyday life, and to your survival, Newsmax Health wants you to have the best and most cutting-edge heart health information available to you today.
That’s why we’re going to send you three of Dr. Crandall’s special reports as a BONUS GIFT when you claim your FREE BOOK, The Healing Powers of Superfoods, today.
Special FREE Report #1:
Get Heart Benefits From Nature’s Bounty Prescription use is rampant and it’s not all good news for pharmaceutical pill poppers. An alarming number of medications do more harm than good. The good news is there are safe, healthy alternatives in nature.
Inside this special FREE report, Dr. Crandall reveals which natural supplements you need to help keep your heart in top condition. You’ll discover the best supplements to help lower cholesterol, steer clear of dangerous heart events, lower blood pressure, fight inflammation, and boost your heart health. Get your information straight from the doctor!
Special FREE Report #2:
7 Tips for Reversing Diabetes More than 30 million Americans have diabetes. It’s the seventh-leading cause of death in the United States and it’s riddled with complications for everything from your eyes and feet to your kidneys and your heart. What can you do?
Turn to Dr. Crandall’s special report to get the seven top tips for reversing diabetes and find out the best time to be tested for it. Clear the confusion about insulin resistance and find out how to make the right dietary choices to prevent diabetes from worsening.
Special FREE Report #3:
Don’t Fall for Fad Diets It’s easy to be lured into the fad diet trap — but according to Dr. Crandall, fad diets don’t work. In this special report, Dr. Crandall reveals the simple-to-follow, seven-point “life plan diet” he gives his own patients.
Also inside your FREE REPORT: Discover the surprising but little-known risk to your heart from poor sleep, as well as the truth about whether intermittent fasting can help you lose weight. Get Dr. Crandall’s top advice on the best diets for your heart for a long and healthy life.
That’s not all!
In addition to your FREE BOOK, The Healing Powers of Superfoods ($16.95 value), and your 3 FREE BONUS REPORTS ($30 value) . . .
Newsmax would like to give you 3 FREE ISSUES of Dr. Crandall’s Heart Health Report.
Because nutrition is so important to keeping your heart healthy, we’d like to give you a three-month trial subscription to Dr. Crandall’s Heart Health Report as an added bonus.
You get 3 FREE TRIAL ISSUES so you can decide if it’s for you. We’re confident you’re going to love it, which is why we can make this incredible offer.
Dr. Crandall’s Heart Health Report keeps you up to date with this top cardiologist’s latest and best heart health advice for preventing, even reversing, heart disease. You get the same top-of-the-line recommendations and insights he gives his own patients — many of whom travel from all around the globe to see him at his Florida clinic.
Each month, Heart Health Report provides you with Dr. Crandall’s expert tips and strategies to help you:
- Slash your risk for a heart attack . . .
- Keep your blood pressure healthy and in check . . .
- Lower your cholesterol safely and easily — without statins . . .
- Minimize your need for dangerous medical procedures like bypass surgery . . .
- Keep your heart ticking smoothly for a long and healthy life . . .
- And more, much more . . .
Read what some of Dr. Crandall’s readers have to say . . .
I have been on a quest for health. I am now 61 years young and was starting on the slippery slope of prescription drugs when diagnosed with hypertension. Once I turned 50 I put on a couple of pounds each year until I found myself weighing in at 191 pounds. As my weight increases so did my blood pressure. Because of my concern for my heart, I have valued the counsel and the advice of Dr. Crandall. I have purchased his book and read every Heart Health Report. I have been making changes in my lifestyle recommended by Dr. Crandall. As for me, following Dr. Crandall’s recommendations, I have dropped weight and my blood pressure has normalized. — Debbie, Georgia
I read Crandall’s Heart Health Report cover to cover and don’t put it down until I’m finished. I get lots of beneficial information from it. So does my wife. I’m always copying certain articles and send those out to family and friends. — George, Arizona
I have followed a great deal of the Dr. Crandall’s recommendations, from supplementation, to diet and exercise. I’m coming up on my 4th anniversary of my “come to Jesus moment”, and am now in the best shape I’ve been in in over 20 years. My doctor won’t allow me to eliminate my meds, but has reduced my dosages, as my numbers (BP, HDL, LDL) are superior. I have, against my doctor’s orders, taken up running and have been competing in 5K runs for over 2 years now. — Jeff, California
Plus, Gain Access to Dr. Crandall’s Online Archive
Covering All Major Cardiovascular Health Threats
This archive is worth its weight in gold! Take advantage of this special offer and you also get three full months — 90 days — of valuable access to the web archives of Heart Health Report, where you’ll find over 100 issues packed full of important heart health information and innovations.
At your leisure (any time, day or night), you can read about topics such as:
- 8 Steps to Reducing High Blood Pressure
- Statins Not the Only Way to Lower Cholesterol
- Women’s Biggest Killer — Know the Signs
- Heart Tests That Can Save Your Life
- What You Must Do to Prevent a Stroke
- Stop Alzheimer’s With a Healthy Heart
- Unclog Your Arteries Without Painful Surgery
Of course, you’re not under any obligation to continue with Heart Health Report. Your satisfaction is guaranteed.
End the confusion of what’s a superfood and what’s not . . . what you should or shouldn’t eat for vibrant health and a long life. Turn your body into a fortress of good health with The Healing Powers of Superfoods — and get the BEST heart health advice from top cardiologist Dr. Chauncey Crandall.
So, let’s recap:
With this Special Offer, you’ll receive your FREE Heart Healthy Superfoods Kit (a $70 value) which includes:
- Cal Orey’s bestselling book:The Healing Powers of Superfoods — a $16.95 value, yours FREE
- Bonus Report #1: Get Heart Benefits From Nature’s Bounty — a $9.99 value, yours FREE
- Bonus Report #2: 7 Tips for Reversing Diabetes — a $9.99 value, yours FREE
- Bonus Report #3: Don’t Fall for Fad Diets — a $9.99 value, yours FREE
- PLUS: A 3-Month, Risk-Free Trial Subscription to Dr. Chauncey Crandall’s Heart Health Report and Access to the Online Archives — All FREE for a Full 90 Days!
Simply Fill Out the Form Below
to Claim Your FREE Gifts Now
Satisfaction Fully Guaranteed!
You are under NO obligation to continue with either subscription. Your subscriptions are risk-free. And again, regardless of your decision, your book, special reports, and any newsletter issues you’ve received are yours to keep.
YES! Please send me my FREE BOOK, The Healing Powers of Superfoods, and 3 FREE digital BONUS reports for just $4.95 shipping & handling now with this special offer. I save over $70 total! Plus, start my 3-month FREE TRIAL subscription to Dr. Crandall’s Heart Health Report right away.
IMPORTANT NOTE: Sales tax included where applicable. Your trial subscription comes with convenient automatic renewal. At the end of your subscription, we’ll notify you. If you want to keep the publication, do nothing and we’ll renew your subscription using your credit/debit card on file and charge you $54.95 for one year (12 issues) of Heart Health Report and thereafter at the lowest renewal rate then in effect. There is no risk; you can cancel within the first six months for a full refund of the unused portion of your subscription.
YES! Please send me my FREE BOOK, The Healing Powers of Superfoods, and 3 FREE digital BONUS reports for just $4.95 shipping & handling now with this special offer. I save over $70 total! Plus, start my 3-month FREE TRIAL subscription to Dr. Crandall’s Heart Health Report right away.
Satisfaction Fully Guaranteed!
You are under NO obligation to continue with either subscription. Your subscriptions are risk-free. And again, regardless of your decision, your book, special reports, and any newsletter issues you’ve received are yours to keep.
Simply fill out the form below.
IMPORTANT NOTE: Sales tax included where applicable. Your trial subscription comes with convenient automatic renewal. At the end of your subscription, we’ll notify you. If you want to keep the publication, do nothing and we’ll renew your subscription using your credit/debit card on file and charge you $54.95 for one year (12 issues) of Heart Health Report and thereafter at the lowest renewal rate then in effect. There is no risk; you can cancel within the first six months for a full refund of the unused portion of your subscription.
Billing Information


The aim of this task is to count the number of solar panel modules in solar installations (generators) mapped in OpenStreetMap.
The solar installation should be positioned in the centre of the image, with a yellow circle around it. However, the imagery provided may not perfectly align with the position of the generator, and it may extend outside the circle.
Normally a solar generator is a consistent block of modules - if a building has generators on two separate roof faces they should be mapped separately. Smaller circles are drawn around other solar installations visible in the image to assist with identifying the correct one.
Type in the number of modules and hit "Submit" or press enter to see the next image. If the individual modules are not visible, if there are too many to easily count, or if it's not clear which generator is tagged, press "Skip" or just press enter without entering a number of modules.


First lets talk about what version control is and why it is useful. Version control is in its simplest the repeated backing-up of your project after you make changes. You can do version control on pretty much anything that you can save, because really it is just saving and storing different stages of your project. It is useful for many reasons, foremost being that if you mess-up your program and save it you can always go back to an earlier version. Now version control (VC) can be done in many ways, one of the most popular programs for it and the one we will be talking about is git. Git makes it incredibly easy to do version control on any kind of project. With git all you have to do is initiate tracking of a file through the command line and every time you want to save a version of the project, just enter one command on the command line and BAMB you have a new version that you can always go back to. The other core git feature is collaboration. Git makes it easy for multiple people to work on the same project without interfering with each other’s code. I could be working on one file of the project and you could be working on another and when we both save them, instead of our saves overriding each other git merges the two documents. Now there can be problems with this, but usually it goes pretty well Whenever git is mentioned you hear usually hear github come up as well. Lets be clear on one thing, git is not github. Github is kind of like the Facebook/Dropbox for git. What github does is provide a cloud-based platform where you can store your git files and share them with others. It also allows others to download your code (if its not private) and use it how they want. Also, remember how we talked about collaboration with git earlier. Well github makes collaboration on code project even easier, people can simply download you code make edits and then send a pull request. A pull request is like saying to the owner of the code "hey look at this cool thing I added to your code you should merge my code with yours". This works well for providing some accountability about what gets added to a project. This is a very brief overview of git and github and as the title of the blog says I am new to this stuff too so my explantion could be a little off, if so please let me know in the comments!


Back
"
Hello,
I am Kim Swift!
I am
very well known for being a great video game designer.
I'm going to show my typical day as a game developer."
Follow My Journey


Watcher of Antharas Gilmore:
You think this skull is from a
Black Dagger Wing
? You're correct!
"What about the other skulls?"
"Goodbye."


Sask. Country Showcase unites top country musicians for charity
by Matt OlsonThis year's Sask Country Showcase brings country musicians from around the province for a charity show at TCU Place on Nov. 21.
Some of the best country artists in Saskatchewan and Canada will take the stage next week in support of Saskatoon’s children.
This year’s Sask Country Showcase brings together the talents of top-notch country musicians for one night of partying and entertainment in support of the Crisis Nursery run by the Saskatoon Society for the Protection of Children. The show happens at TCU Place on Thursday, Nov. 21.
Here are five things to know about the showcase.
1. The artists
The lineup is pretty loaded with local talent, from the likes of the Juno Award-winning Jess Moskaluke to rapidly rising star Tenille Arts and more. The full list of performers includes Moskaluke, Arts, Aaron Pritchett, Aaron Goodvin, Jason Blaine, Bobby Wills, Jay Semko, and Samara Yung. Two more musicians — Michele Dubois and Tyler Lewis — are set to play during the pre-show.
2. The cause
The Saskatoon Society for the Protection of Children runs two programs in the city: the Crisis Nursery and Parent Aide. Both programs feed into the mission statement of the society of strengthening and preserving families through preventative and intervening actions. The showcase has become the main fundraiser for the Crisis Nursery in recent years.
“We wanted something that was unique … that would create more community,” SSPC and country showcase board member Gregg Bamford said. “It’s as much about fundraising as creating awareness.”
Related
3. The event
The party starts at 6 p.m. at TCU Place with an hour of cocktails before the show kicks off in earnest at 7. Besides playing tunes for the crowd, the artists will also have opportunities to share stories about how songs were created and their musical process.
4. The history
The Sask Country Showcase started as a much smaller event at Mayfair Church years ago; the 2018 show was the first time in the bigger venue of TCU Place. Ticket sales are already on track to exceed last year’s show, and joint Sask Country Showcase and SSPC board member Alice Kuipers said they’ve been trying to grow the event since it became the main fundraiser for the Crisis Nursery.
“Building community and tying music together, for all of us, is a really great way to spend an evening,” Kuipers said. “Those kids being with their families makes the city a better place to be.”
5. The goal
Bamford said the Crisis Nursery needs to raise about $350,000 each year to keep things running — and while this event won’t cover all of its needs, Bamford and Kuipers said the more visibility they can get on the event, the more it can grow. Between the pre-show, the full showcase and the after-party at the nearby Holiday Inn, there’s plenty to draw people in.
“This is a growing year,” Bamford said. “We’ve got 10 amazing artists … we hope to bring people to Saskatoon and showcase our amazing city.”


Seismic risk:
Low ▉ ▉ ▉ ▉ ▉ High
Circles show significant quakes, from 1965 to present, scaled by the amount of ground shaking. Click/tap for details.
Tap quakes for details.
Source: U.S. Geological Survey
I followed Peter Aldhous' tutorial to build this.


@* * This template takes a single argument, a String containing a * message to display. *@ @(message: String, characterSet: String) @* * Call the `main` template with two arguments. The first * argument is a `String` with the title of the page, the second * argument is an `Html` object containing the body of the page. *@ @main("Word Scramble Game") { @* * Get an `Html` object by calling the built-in Play welcome * template and passing a `String` message. *@


Amazon Cognito
Amazon Cognito is a web service that facilitates the delivery of
scoped, temporary credentials to mobile devices or other untrusted
environments. Amazon Cognito uniquely identifies a device or user
and supplies the user with a consistent identity throughout the
lifetime of an application.
Amazon Cognito lets users authenticate with third-party identity
providers (Facebook, Google, or Login with Amazon). As a
developer, you decide which identity providers to trust. You can
also choose to support unauthenticated access from your
application. Your users are provided with Cognito tokens that
uniquely identify their device and any information provided about
third-party logins.
-
APIVersion = '2014-06-30'
-
DefaultRegionEndpoint = 'cognito-identity.us-east-1.amazonaws.com'
-
DefaultRegionName = 'us-east-1'
-
ResponseError
alias of JSONResponseError
-
ServiceName = 'CognitoIdentity'
-
TargetPrefix = 'AWSCognitoIdentityService'
-
create_identity_pool(identity_pool_name, allow_unauthenticated_identities, supported_login_providers=None)
Creates a new identity pool. The identity pool is a store of
user identity information that is specific to your AWS
account.
| Parameters: |
- identity_pool_name (string) – A string that you provide.
- allow_unauthenticated_identities (boolean) – TRUE if the identity pool
supports unauthenticated logins.
- supported_login_providers (map) – Optional key:value pairs mapping
provider names to provider app IDs.
|
-
delete_identity_pool(identity_pool_id)
Deletes a user pool. Once a pool is deleted, users will not be
able to authenticate with the pool.
| Parameters: | identity_pool_id (string) – An identity pool ID in the format REGION:GUID. |
-
describe_identity_pool(identity_pool_id)
Gets details about a particular identity pool, including the
pool name, ID description, creation date, and current number
of users.
| Parameters: | identity_pool_id (string) – An identity pool ID in the format REGION:GUID. |
-
get_id(account_id, identity_pool_id, logins=None)
Generates (or retrieves) a Cognito ID. Supplying multiple
logins will create an implicit linked account.
| Parameters: |
- account_id (string) – A standard AWS account ID (9+ digits).
- identity_pool_id (string) – An identity pool ID in the format REGION:GUID.
- logins (map) – A set of optional name/value pairs that map provider
names to provider tokens.
|
-
get_open_id_token(identity_id, logins=None)
Gets an OpenID token, using a known Cognito ID. This known
Cognito ID is returned from GetId. You can optionally add
additional logins for the identity. Supplying multiple logins
creates an implicit link.
| Parameters: |
- identity_id (string) – A unique identifier in the format REGION:GUID.
- logins (map) – A set of optional name/value pairs that map provider
names to provider tokens.
|
-
list_identities(identity_pool_id, max_results, next_token=None)
Lists the identities in a pool.
| Parameters: |
- identity_pool_id (string) – An identity pool ID in the format REGION:GUID.
- max_results (integer) – The maximum number of identities to return.
- next_token (string) – A pagination token.
|
-
list_identity_pools(max_results, next_token=None)
Lists all of the Cognito identity pools registered for your
account.
| Parameters: |
- max_results (integer) – The maximum number of identities to return.
- next_token (string) – A pagination token.
|
-
make_request(action, body)
-
unlink_identity(identity_id, logins, logins_to_remove)
Unlinks a federated identity from an existing account.
Unlinked logins will be considered new identities next time
they are seen. Removing the last linked login will make this
identity inaccessible.
| Parameters: |
- identity_id (string) – A unique identifier in the format REGION:GUID.
- logins (map) – A set of optional name/value pairs that map provider
names to provider tokens.
- logins_to_remove (list) – Provider names to unlink from this identity.
|
-
update_identity_pool(identity_pool_id, identity_pool_name, allow_unauthenticated_identities, supported_login_providers=None)
Updates a user pool.
| Parameters: |
- identity_pool_id (string) – An identity pool ID in the format REGION:GUID.
- identity_pool_name (string) – A string that you provide.
- allow_unauthenticated_identities (boolean) – TRUE if the identity pool
supports unauthenticated logins.
- supported_login_providers (map) – Optional key:value pairs mapping
provider names to provider app IDs.
|


3 references to RequiresPSEditions
System.Management.Automation (3)
engine\CommandDiscovery.cs (3)
449
if (scriptInfo.
RequiresPSEditions
!= null)
453
foreach (var edition in scriptInfo.
RequiresPSEditions
)
467
var specifiedEditionsString = string.Join(",", scriptInfo.
RequiresPSEditions
);


Five common issues with services and dry-monads
19 May 2020Today I found the source code of a simple test task in GitHub. The code author used dry-monads, services, and other abstractions. I found some issues in that code and recalled that I have seen the same issues in other projects. That’s why I think that it’s a common problem and it will be a good idea to make a review and explain how we can improve our code. Also, I expect that this blog post can be useful for “junior/middle” developers and it could be boring for “senior” developers.
Important
This blog post based only on my personal experience and I don’t want to write the “best” code in the world. The main point which I want to provide - share my knowledge and experience for everyone else. Also, I don’t want to say that you need to use services or dry-monads. But if you use it you can potentially face issues that I tried to describe in this blog post.
I asked the code author about this publication and he allowed me to do it.
TLDR
Problems:
- Naming based on implementation;
- Redundancy of service objects;
- Using of generic names;
- State inside a class and mix of parameters and dependencie;
- ActiveModel’s chain of calls in business logic;
Original code
The main operation (service) which we call from rails controller looks like this:
module Groups
module Services
class JoinUser < Service
def initialize(params, record: ,
input_validator: Users::Validators::NewUser.new,
user_exists_check: Users::Services::CheckExists,
user_creator: Users::Services::Create
)
@params = params
@input_validator = input_validator
@record = record
@user_creator = user_creator
@user_exists_check = user_exists_check
end
def call
validated_params = yield input_validate
yield business_validate(validated_params.to_h)
save(validated_params.to_h)
end
private
attr_reader :params, :record, :input_validator, :user_creator, :user_exists_check
def input_validate
input_validator.call(params[:group]).to_monad
end
def business_validate(validated_params)
return Failure(:already_exists) if record.users.where(validated_params).exists?
Success()
end
def save(validated_params)
user_check_result = user_exists_check.new(validated_params[:email]).call
if user_check_result == Success(:user_not_exists)
user = yield user_creator.new(validated_params).call
record.users << user
return Success(:created)
end
record.users << user_check_result.value!
Success(:created)
end
end
end
end
Also, we use two dependencies, which look like this:
module Users
module Service
class CheckExists < Service
def initialize(email, model: User)
@email = email
@model = model
end
def call
return Success(model.find_by(email: email)) if model.where(email: email).exists?
Success(:user_not_exists)
end
private
attr_reader :email, :model
end
end
end
module Users
module Services
class Create < Service
def initialize(params,
input_validator: Users::Validators::NewUser.new,
model: User
)
@params = params
@input_validator = input_validator
@model = model
end
def call
yield input_validate
save
end
private
attr_reader :params, :model, :input_validator
def input_validate
input_validator.call(params).to_monad
end
def save
Try() { model.create!(params) }
end
end
end
end
Problems
After reading and understanding the original code I found five places which I want to describe:
- Naming based on implementation;
- Redundancy of service objects;
- Using of generic names;
- State inside a class and mix of parameters and dependencie;
- ActiveModel’s chain of calls in business logic;
Let’s discuss each part.
Naming based on the implementation
I think a good starting point for refactoring this code is to remove “out of context” words from variables, objects, and services names.
Quick example: you can see save(validated_params.to_h)
(Groups::Services::JoinUser
service) line in the original code. After some research, I realized that the original context is enrolling users in groups. I mean that from the implementation perspective this line should update something in DB. But from business logic, the service has an absolutely different context. That’s why I think that #enroll
will be better than #save
here.
If you want to use implementation details in naming you should be ready to spend more time understanding the context of each method/service and thinking why we have this code here. But if you use naming based on business flow you provide a context that can answer the question “why?” and you can reduce time spent on understanding the source code. I think that domain-driven design (DDD in the future) is trying to give the same idea related to naming, that’s why if you want to read more about it - DDD books will be a good start (and event storming for understanding better all business events in the system).
Redundancy of service objects
I can see two global issues in the original source code:
- The main service depend on a lot of different dependencies (like
Users::Validators::NewUser.new
,Users::Services::CheckExists
,Users::Services::Create
and direct model calls likerecord.users.where(validated_params).exists?
); - Both called services implements only DB logic (checking for the existence of the record and saving data in DB);
Let’s start with DB request logic. It’s a great temptation is to make 2 separate services and put DB logic there, like the author of the original example did. I can understand it because you want to isolate parts of your service. But this way has a downside - your business logic looks complicated. For example, let’s check two lines in Groups::Services::JoinUser#save
method:
def save(validated_params)
user_check_result = user_exists_check.new(validated_params[:email]).call
# We need to execute strange comparation logic with Result monad here
#
# The logic is strange because the monad looks strange in current context. It's complicated to say why `user_not_found` is a success result.
# We need bussines logic knowledge to say why it is
if user_check_result == Success(:user_not_exists)
# We need to use DO notations in the nested method in the current case.
# That's why we'll get nesting and it can be possible to increase the complication of understanding the data flow in the system
user = yield user_creator.new(validated_params).call
I can see that the author wrapped up the database operation into nested DO notation (which not important in this case) and thus inflated his own code. That’s why it looks like those same methods should be in model/repository (if you use repository pattern). Also, we come to the context issue based on the naming of methods, because the service names describe implementation details instead of business steps/logic.
Using of generic names
You can find a lot of base words in the example like record
, user_exists_check
, validated_params
, business_validate
, input_validate
, params
and others. I covered it in the first issue. It’s really hard to understand what these objects/methods do and why we need it without a full context of logic.
For example, read naming for two services and try to answer what each service do here:
Groups::Services::JoinUser
Groups::Services::EnrollUser
When I see JoinUser
I expect that we take a user from data storage and just join them to a group. But in real world we need to enroll a user to a specific group.
State inside a class and mix of parameters and dependencie
In the original example, the author used dependency injection (DI in the future) for calling other services and validations. It’s a powerful technique with helps with testing and paintability of your project (I suggest to check posts or talks from solnic, Tim, and Luca if you don’t use or see it before).
As you can see the author puts data and dependencies inside the class constructor (#initialize
) in each service.
def initialize(params, record:, # data
input_validator: Users::Validators::NewUserContract.new, # dependency
user_exists_check: Users::Services::CheckExists, # dependency
user_creator: Users::Services::Create # dependency
)
def initialize(email, model: User)
@email = email # data
@model = model # dependency
end
def initialize(params, # data
input_validator: Users::Validators::NewUserContract.new, # dependency
model: User # dependency
)
It’s not a problem in general. But if you use complicated dependencies with long time building (for example rom) or want to use DI in testing you’ll get a problem. Also, you’ll allocate new objects every action calls, but it’s not a critical.
The most critical part of this code states inside a class because you get two issues:
You’re losing pure objects conception. It means that you can possibly get bugs related to mutation of your state.
This solution is increasing maintainability of the code. For example, check these two methods from the service object:
def save(validated_params)
def save(record, validated_params)
In the first method, we see save
method plus validated_params
. Based on this knowledge I can say that the method just store something in the base and that’s all. In the second method, we see that method saves something related to the record
. The problem here in that method really uses record
data based on the state of the service inside self but we can’t understand it without reading the code.
ActiveModel’s chain of calls in business logic
I didn’t use rails last 3 years but I can understand that It’s a common approach in rails. I’m talking about lines with record.users.where(payload).exists?
or something like this. I see two problems in this approach:
- It’s hard to use unit tests for this because you need to mock a chain of methods. It’s not a problem if you’re using only integration testing but integration testing increases time for your test suite because you use DB calls every time.
- The issue connected to the first point, you need to make transformation AR DSL to a specific context. For example, say what is better to understand
record.users.where(payload).exists?
orrecord.attendee?(payload)
.
Finnal result
module Cource
module Services
class EnrollUser < Core::Service
attr_reader :model, :validator
def initialize(model: User, validator: Users::Validators::NewUserContract.new)
@model = model
@validator = validator
end
def call(group, params)
payload = yield validate_raw_data(params)
yield group_attendee?(group, payload)
user = yield find_or_create_by_email(payload[:email])
success(group.enroll(user))
end
private
def validate_raw_data(payload)
# don't forget to enable monads extention
# Dry::Validation.load_extensions(:monads)
#
# https://dry-rb.org/gems/dry-validation/master/extensions/
validator.call(payload).to_either
end
def group_attendee?(group, payload)
group.attendee?(payload) ? failure(:group_attendee) : success()
end
def find_or_create_by_email(payload)
Try() { model.find_or_create(payload) }.to_result
end
end
end
end
class Group
def attendee?(payload)
users.where(payload).exists?
end
def enroll(user)
self.users << user
self
end
end
class User
def self.find_or_create(payload)
find_by_email(payload[:email]) || create!(payload)
end
end
You can see that I got a different code based on issues which I tried to describe on the top. In this code you can see business steps that you need to do to make something happen (validation -> user verification for specific source group -> enrolling user
). Also, I dropped unnecessary services and move logic to models with context related names.
This code is not great, also, different developers will use different ways to write this logic and I’m not sure that “true” way exists here. But if you compare services you see that sometimes a better name is more than separate service and one more abstraction.
Also, you will get a code that can be covered by unit tests without any pain. For example, you can test business logic in #group_attendee?
without any integration tests and complicated preparation of the data:
RSpec.describe Cource::Services::EnrollUser, type: :service do
subject { service.call(group: group, params: params) }
let(:service) { described_class.new(model: model, validator: validator) }
let(:model) { ... }
let(:validator) { ... }
context 'when user is an attendee for a group' do
let(:group) { instance_double('Group', attendee?: true) }
# your tests for one case of logic
end
context 'when user is not an attendee for a group' do
let(:group) { instance_double('Group', attendee?: false) }
# your tests for other case of logic
end
context 'test case with real dependencies for test everythign together' do
subject { service.call(...) }
let(:service) { described_class.new }
# some data preporation
it { expect(subject).to be_success }
end
end
- Note to testing part: I’m thinking that you should use the unit and integration testing for full coverage. In my example, I’ll also add 1+ integration test for test all contracts between dependencies to be sure that everything is okay with real code and data.
Conclusions
- Use context instead of implementation in your code. Context is really important from a maintainability perspective and your colleagues and you from the future say “thank you”. Also, you can skip unnecessary questions on PR/MR review process and make it faster;
- Using a good naming is extremely hard. I spent more than 3 years to start writing something understandable for other developers. I can suggest some rules which help me every day;
- Imagine a real world process. For example in the original example try to imagine how this logic should work from a user perspective. Forget about CRUD or SQL implementation. Just write real steps on how a user can do something. In my case, I wrote, “as a user if I have valid data in my form or profile AND if I didn’t attend this course group I can attend”. Based on these words I found important steps and used this note for creating names for my methods/services;
- If you want to use common words like
user_exists_check
,input_validate
, etc. make a pause and ask yourself what you want to explain from real world perspective; - This approach takes some time and can be difficult. But it can be good practice for improving your skills as a developer. And knowing a domain can be useful in the future to understand tasks or communicate with colleagues better;
- Sometimes is better to use models for DB logic instead of creating a new service object with common behavior;
- It’s a good idea to split data and dependencies for each service object. In this case, you can avoid unnecessary allocation, cache some initializations and drop mutable state inside objects;
- Using model methods instead of a chain of DSL methods can be profitable from two perspectives: adding a good name for each DB request instead of a “pure” SQL and simplify unit testing and avoiding mocking chain of methods;
Useful links
- dry-monads and DO notations guide - if you never work with it and don’t know how it works;
- Event storming - you can try to adopt this idea to get more context and information about your system;
- Tim’s talk where he described dry-rb ideas and services with separated data and dependencies;
UPD
- Added
validate_raw_data
method. Thanks mpak for mention it (from comments)


"Marko is a "
"fast"
" and lightweight templating engine that compiles templates to CommonJS modules and supports streaming, async rendering and custom tags. It supports both a familiar HTML syntax and a concise, indentation-based syntax."