Interface
When opened you will be presented with the below window:
Help
tips are shown in the bottom box when you hover over certain items
within the interface. If your stuck reading the help box will solve
most problems.
Adding Sources
Before you can
add your games to GameManager you must add the location of where your
games are installed. This can be achieved by clicking File ->
Sources from the top menu.
When you click this you will be greeted
with this window
To add sources use the Add Source box at the bottom left of window, Select the platform you want to select the games for from the drop down box then.
For PC:
To add all the
folders within a folder use the 'Add Contents of Folder' button
To
add a single folder click 'Add Single Folder/File'
For All other platforms:
To add all the
files within a folder use the 'Add Contents of Folder' button
To
add a single file click 'Add Single Folder/File'
Each choice will
be added to the list above the buttons.
Selecting a source from
the list and clicking 'Delete Source' will remove it from the list.
Click 'Done' when you have added the locations of all your games.
Adding Games to Database
You should now have a window similar to below:
You now have two choices:
Use the 'Auto Search' Button which gets the program to make educated guesses as to what game the folder/file is.
Set the games manually
To set games manually click on the folder from the list and then click 'Set Game' (Note: Double clicking game in list does the same). You should then be greeted with the following screen:
If the name of the game is wrong you can use the search button and box at top to search for other results. If your game is listed click on it and then click OK. If you cant find your game on the online database click 'Add A Game To Online Database' to add it online (Note: anything added to online database will instantly be available from this screen).
If you game is
platform type PC and not a steam game you will be greeted with the
next window, otherwise you will return to main screen and can skip
this step.
From
this screen you must select the executable file that starts the game.
If you are unsure which one it is you can use the 'Try EXE' button to
run the specified executable, and if your game runs it is the correct
executable.
Also note the 'Level Up' and 'Level Down' buttons. The list contains the executables from the first folder level containing executables, if the run executable is in a higher level then click those buttons to get the program to find it.
Select the correct executable then click 'Ok'.
Saving Changes
After you have Added the game its values in the main list will change like below:
Note
the column on the left of the list, its value indicates the
following:
M – Game Manually Added via Set Game Button (Unsaved)
A – Game Automatically Added via Auto Search Button (Unsaved)
I – Game Added to Database (Saved)
SM – Steam Game Manually Added via Set Game Button (Unsaved)
SA – Steam Game Automatically Added via Auto Search Button (Unsaved)
SI – Steam Game Added to Database (Saved)
SF – Steam Folder
S – Unadded Steam Game
[blank] – Unadded Game
All unsaved games (M, A, SM, SA) must be saved to local database before they are available in FrontEnd, to do this you need to click the 'Save Changes' Button:
Then
it will work through all the updated games and save them, after it
has finished the list should show all games as installed (I, SI)
Updating Fan Art and Game Information
After your games have been saved to the database they will be accessible from FrontEnd but they wont have any fan art, to get fan art added to GameManager you must click 'Update Installed Games':
this
will work through all installed games and download updated
information along with fan, banner, back and front art, and
screen-shots.


Antfarm is a simple automation framework that aims to make file automation robust and scalable. 🐜
$ npm install antfarm
const Antfarm = require('antfarm'),
af = new Antfarm();
let hotfolder_a = af.createFolderNest("/var/hotfolders/a");
let pdf_folder = af.createFolderNest("/var/out/pdf");
let other_folder = af.createFolderNest("/var/out/others");
let tunnel = af.createTunnel("Simple pdf sorting workflow");
tunnel.watch(hotfolder_a);
tunnel.run((job, nest) => {
if(job.extension == "pdf"){
job.move(pdf_folder);
} else {
job.move(other_folder);
}
});
During late October, much of antfarm was refactored to use TypeScript's accessors. The documentation may be out of date. The documentation in the API reference should always be up to date.
// Old style
job.setName("myFile.pdf");
console.log(job.getName());
// New style, using TypeScript accessors
job.name = "myFile.pdf";
console.log(job.name);
https://github.com/dominickp/antfarm/wiki
Generated using TypeDoc


You can specify exact width for certain column(s). In the example below the width of the Government Form column is 200px. It is also possible restrict the maximum width of a whole grid.


Portfolio optimization (pynance.pf
)¶
-
pynance.pf.
optimize
(exp_rets, covs)[source]¶ Return parameters for portfolio optimization.
Parameters: exp_rets : ndarray
Vector of expected returns for each investment..
covs : ndarray
Covariance matrix for the given investments.
Returns: a : ndarray
The first vector (to be combined with target return as scalar) in the linear equation for optimal weights.
b : ndarray
The second (constant) vector in the linear equation for optimal weights.
least_risk_ret : int
The return achieved on the portfolio that combines the given equities so as to achieve the lowest possible risk.
Notes
- The length of exp_rets must match the number of rows and columns in the covs matrix.
- The weights for an optimal portfolio with expected return ret is given by the formula w = ret * a + b where a and b are the vectors returned here. The weights w for the portfolio with lowest risk are given by w = least_risk_ret * a + b.
- An exception will be raised if the covariance matrix is singular or if each prospective investment has the same expected return.


To do list
Add todo item
EAT
FINISH
DELETE
SLEEP
DELETE
CODING
DELETE
FINISHED 1/3
FINISH SELECTED
DELETE SELECTED


Team members: Vipul Munot Anirudh Murlidhar Siddharth Jayasankar
Nowadays big data is being used in almost all the domains. Sports informatics and analytics is also venturing into big data to perform in-depth analysis in order to come up with better strategies and team composition. Through this project we would like to would wanted to explore how useful effective visualization of data regarding player’s performance could help develop better strategies and also help the player become better at the sport.
The aim of this project is to analyze the career of basketball player Kobe Bryant by producing high quality visualizations. This project explores the use of innovative visualizations for the sport facilitating effective analysis of basketball players .


Reports try finally statements which can use Java 7
Automatic Resource Management. A quickfix is available to convert the try finally
statement into a try with resources statement.
This inspection only reports if the project or module is configured to use a
language level of 7.0 or higher.
New in 10.5, Powered by InspectionGadgets


Stay on track as you plan, develop, and deliver products.
Discover PlatformAbsolutely bladdered he blimey guvnor agency.
Absolutely bladdered he blimey guvnor agency.
Absolutely bladdered he blimey guvnor agency.
Starkers pardon you knees up is Elizabeth geeza Why, quain standard guvnor gosh cras brilliant.
Starkers pardon you knees up is Elizabeth geeza Why.
Looking to start an online store and you're not sure where to begin? I'll guide will help you to navigate Would you like to boost your Twitter profile.
Looking to start an online store and you're not sure where to begin? I'll guide will help you to navigate Would you like to boost your Twitter profile.
Looking to start an online store and you're not sure where to begin? I'll guide will help you to navigate Would you like to boost your Twitter profile.
Over the last few years, podcasts have become a huge deal. They’ve taken on a growing role.
Happy Clients
Projects
Trusted Users


Feni1576.github.io
Gtoys G15 PRO Series Handbücher.
Gtoys G15 PRO Series Handbücher and Playseat Seatslider Montageanleitung.


Our mission is to provide our customers with quality parts and personalized
customer service.
JAV Machine Craft is family owned and operated since 1999. With a wide array
of machinery and specialized support equipment, JAV is ready to help you with all your manufacturing
needs.
Click here to learn about our company and who we are.
Click here to learn more about the services we can provide for you, ranging from prototyping to large quantity production.
Click here to find out how to get in touch with us so we can help you on your next project.


Sup guys, I’m back, this time to talk about Johnny Five, a JavaScript plataform for IoT and Robotic. In this post we will see how to control a Led by HTTP request.
Before we start, for this post It Is necessary that you have an Arduino and at least one Led.
Let’s start installing Johnny-Five and your requirements:
apt-get install nodejs-legacy
apt-get install build-essential
npm install serialport
npm install johnny-five
Now open your text editor and create a new .js file and paste the following code:
var five = require("johnny-five"),
board = new five.Board();
board.on("ready", function() {
// Create an Led on pin 13
var led = new five.Led(13);
// Strobe the pin on/off, defaults to 100ms phases
led.strobe();
});
Connect your Arduino on PC, and the Led in Arduino this way:
Now on terminal, run the folowing command:
sudo node strobe.js
If everything ok, your Led will light up. :-D
Now let’s make things more fun. Create a folder in your machine and execute npm init on this folder. After install the Express, with the following command:
npm install --save express
Create a new JS file called controller.js, and in this file, import the Express and the Johnny Five
const express = require("express");
const five = require('johnny-five');
const app = express();
Now create the following variables, to deal with de board and the status of Led:
var board = new five.Board();
var isReady = false;
var isOn = false;
var led;
Put your Express server to run, this way:
app.listen(3000, () => {
console.log("Servidor rodando na porta 3000");
});
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "*");
next();
});
Now define a route to receive GET Requests:
app.route("/")
.get((req, res) => {
toggleLed();
res.json({status: isOn});
});
When the board Is ready, It’s necessary something to deal with it, for this add the following code on the controller.js:
board.on('ready', function() {
// Define the pin 13 to be used
led = new five.Led(13);
// Turn off the Led
led.off();
// save the actual state of the Led
isReady = true;
});
Now add the code for the toggleLed() method, called in the app.route.
function toggleLed () {
// If the Led is on
if (isOn) {
//Turn off the Led
led.off();
isOn = false;
} else {
//Turn on the Led
led.on();
isOn = true;
}
}
The complete code can be found on my GitHub here.
Now on terminal run the folowwing command:
sudo node controller.js
Access localhost:3000 on browser and see the magic happening.
To improve our experience, let’s create a client application for turn on/off the led. Create a new folder on your machine, and create the folder img, and the files index.html, index.css and index.js.
Inside the folder img, add the images here.
On the css file, add the following code:
.div-buttons {
display: flex;
justify-content: center;
padding-top: 20%;
}
.buttons {
width: auto;
height: 140px;
}
.img-led {
height: 70px;
width: auto;
}
.title {
display: flex;
justify-content: center;
}
#text-led {
font-family: Arial, Helvetica, sans-serif;
font-size: 20pt;
text-transform: uppercase;
}
body {
background-color: beige;
background-image: linear-gradient(to right,#e5e5ff, #ccccff);
}
Inside the html file, add the following code:
<!DOCTYPE html>

<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="index.css">
<title>Led Controller</title>

Now on the js file, let’s create a function to made get requests to our api, using the axios library, imported in our html file:
function consultarStatusLed(){
let url = 'http://localhost:3000/';
axios.get(url, conf)
.then(function (response) {
changeButtons(response.data.status);
})
.catch(function (error) {
// handle error
console.log(error);
})
}
Now create a function to request the change of the state of the Led, and change the button on page, for this add the following code:
const $ = document.querySelector.bind(document);
function changeButtons(valor){
if(valor == false){
$(".div-buttons").innerHTML = `<input type="image" src="img/on.png" class="buttons" onclick="mudarStatusLed()">`;
$(".title").innerHTML = `

Led Controller

`;
}else {
$(".div-buttons").innerHTML = `<input type="image" src="img/off.png" class="buttons" onclick="mudarStatusLed()">`;
$(".title").innerHTML = `

Led Controller

`;
}
}
function mudarStatusLed(){
consultarStatusLed();
}
Lastly add a call to consultaStatusLed() on the end of the file:
consultarStatusLed();
The complete code can be found in my GitHub here.
Save everything and let’s test. Open index.html in a browser and click the on / off button a few times, if everything went well, the result will be as show in the video link below:
That’s all folks, until next time!
Written on December 1st , 2018 by Hallessandro D´villa


“Animals are such agreeable friends—they ask no questions; they pass no criticisms.”
George Eliot, British Author
I am the major domo for two feisty ferrets, born September 2017. Lucas is a ferret-polecat mix while
George is a domestic ferret from Marshall farms.
Ferrets are very playful with natural curiosity. Lucas weighs in at almost four pounds with tremendous
athletic ability...this is a challenge in the house.


Seems like you´re not authenticated. Please try again later.
You will be redirected to the last visited page in 5 seconds.


The following parameters control the matrix generation and search algorithm.
Size and Wall% affect the "reset". The other three values affect the search. While Max Nodes is the maximum number of nodes expanded per iteration the weights are multipliers to the distance and heuristic function. A bigger weight means the algorithm will "prefer" minimizing that component.
| Size: | Max Nodes: | Distance Weight: | |||||
| Wall%: | % | Heuristic Weight: |
Iteration: 0 Nodes: 0 Time: 0 ms / max = 0 ms


To start, upload the CSV/JSON/XML/HTML file into RAMADDA. Go to the Main Menu->View->Convert Data
menu to see the conversion interface below. A pipeline of commands are defined for manipulating the rows and columns of the source file.
These commands can be on the same line or on multiple lines. If on multiple lines then the intermediate processing results of each line of commands is available. Prefix a line with "#" to comment out the line.
"colA,colB,colC-colD,colE,..." e.g.: "0,1,2,7-10,12"
-html "name value arguments"The name/value arguments include:
-html "skip <how many tables in the HTML file to skip>" -html "removePattern <regexp pattern to remove> removePattern2 <another pattern to remove>"Because there are many levels of parsing of escape characters if you need to remove text that contains special regular expression characters - [,],(,) and . - use the special tags: _leftbracket_, _rightbracket_, _leftparent_, _rightparen_, _dot_, e.g.:
-html "removePattern _leftbracket_.*?_rightbracket_ "
-columns <columns(Column indices, one per line.Only include the given columns Usage:
Can include ranges, e.g. 0-5) >
-notcolumns <columns(Column indices, one per line.Don't include the given columns Usage:
Can include ranges, e.g. 0-5) >
-delete <columns(Column indices, one per line.Remove the columns Usage:
Can include ranges, e.g. 0-5) >
-cut <rows(One or more rows. -1 to the end. e.g., 0-3,5,10,-1) >Usage:
-include <rows(one or more rows, -1 to the end) >Only include specified rows Usage:
-skip <rows(How many rows to skip) >Skip number of rows Usage:
-copy <column> <name>Usage:
-insert <column(Column to insert after) > <values(Single value or comma separated for multiple rows) >Insert new column values Usage:
-concat <columns(Column indices, one per line.Create a new column from the given columns Usage:
Can include ranges, e.g. 0-5) > <delimiter>
-split <column> <delimiter(What to split on) > <names(Comma separated new column names) >Split the column Usage:
-splat <key col> <column> <delimiter> <name(new column name) >Create a new column from the values in the given column Usage:
-shift <rows(Rows to apply to) > <column(Column to start at) > <count>Shift columns over by count for given rows Usage:
-addcell <row> <column> <value>Add a new cell at row/column Usage:
-deletecell <row> <column>Delete cell at row/column Usage:
-mergerows <rows(2 or more rows) > <delimiter> <close>Usage:
-rotateRotate the data Usage:
-flipReverse the order of the rows except the header Usage:
-unfurl <column(column to get new column header#) > <value columns(Columns to get values from) > <unique column(The unique value, e.g. date) > <other columns(Other columns to include) >Make columns from data values Usage:
-furl <columns(Column indices, one per line.Use values in header to make new row Usage:
Can include ranges, e.g. 0-5) > <header label> <value label>
-explode <column>Make separate files based on value of column Usage:
-join <key columns> <value_columns(value columns) > <file(File to join with) > <source_columns(source key columns) >Join the 2 files together
-start <start pattern>Start at pattern in source file Usage:
-stop <stop pattern>End at pattern in source file Usage:
-rawlines <lines(How many lines to pass through unprocesed) >Usage:
-min <min # columns>Only pass thorough lines that have at least this number of columns Usage:
-max <max # columns>Only pass through lines that have no more than this number of columns Usage:
-pattern <column> <pattern>Pass through rows that match the pattern Usage:
-notpattern <column> <pattern>Pass through rows that don't match the pattern Usage:
-unique <columns(Column indices, one per line.Pass through unique values Usage:
Can include ranges, e.g. 0-5) >
-dups <columns(Column indices, one per line.Pass through duplicate values Usage:
Can include ranges, e.g. 0-5) >
-maxvalue <key column> <value column>Usage:
-eq <column> <value>Extract rows that pass the expression Usage:
-gt <column> <value>Extract rows that pass the expression Usage:
-ge <column> <value>Extract rows that pass the expression Usage:
-lt <column> <value>Extract rows that pass the expression Usage:
-le <column> <value>Extract rows that pass the expression Usage:
-before <column> <format> <date> <format2>Usage:
-after <column> <format> <date> <format2>Usage:
-countvalue <column> <count>No more than count unique values Usage:
-decimate <rows(# of start rows to include) > <skip(skip factor) >only include every <skip factor> row Usage:
-skipline <pattern>Skip any line that matches the pattern
-change <columns(Column indices, one per line.Change columns Usage:
Can include ranges, e.g. 0-5) > <pattern> <substitution string(use $1, $2, etc for pattern (...) matches) >
-changerow <rows(Row indices, one per line.Change the values in the row/cols Usage:
Can include ranges, e.g. 0-5) > <columns(Column indices, one per line.
Can include ranges, e.g. 0-5) > <pattern> <substitution string>
-set <columns(Column indices, one per line.Write the value into the cells Usage:
Can include ranges, e.g. 0-5) > <rows(Row indices, one per line.
Can include ranges, e.g. 0-5) > <value>
-macro <pattern> <template> <column label>Look for the pattern in the header and apply the template to make a new column, template: '{1} {2} ...', use 'none' for column name for no header Usage:
-setcol <column(match col #) > <pattern> <write column> <value>Write the value into the write col for rows that match the pattern Usage:
-priorprefix <column> <pattern> <delimiter>Append prefix from the previous element to rows that match pattern Usage:
-letterAdd 'A','B', ... as column Usage:
-case <type> <column>Change case of column Usage:
-width <columns(Column indices, one per line.Limit the string size of the columns Usage:
Can include ranges, e.g. 0-5) > <size>
-prepend <text>Add the text to the beginning of the file. use _nl_ to insert newlines Usage:
-pad <count> <pad string>Add or remove columns to achieve the count Usage:
-prefix <column> <prefix>Add prefix to column Usage:
-suffix <column> <suffix>Add suffix to column Usage:
-js <javascript>Define Javascript to use later Usage:
-func <names(New column names) > <javascript(javascript expression) >Apply the function. Use column names or _col# Usage:
-endswith <column> <string>Ensure that each column ends with the string Usage:
-trim <columns(Column indices, one per line.Trim the string values Usage:
Can include ranges, e.g. 0-5) >
-convertdate <column> <sourceformat(Source format) > <destformat(Target format) >Usage:
-extractdate <date column> <format(Date format) > <timezone> <what(What to extract) >Usage:
-formatdate <columns(Column indices, one per line.Usage:
Can include ranges, e.g. 0-5) > <intial date format> <target date format>
-truncate <column> <max length> <suffix>Usage:
-extract <column> <pattern> <replace with(use 'none' for no replacement) > <new column name>Extract text from column and make a new column Usage:
-map <column> <new columns name> <value newvalue ...>Change values in column to new values Usage:
-combine <column> <delimiter> <new column name>Combine columns with the delimiter. deleting columns Usage:
-combineinplace <column> <delimiter> <new column name>Combine columns with the delimiter Usage:
-format <columns(Column indices, one per line.Usage:
Can include ranges, e.g. 0-5) > <format(Decimal format e.g. '##0.00') >
-denormalize <file(From csv file) > <from id idx> <from value idx> <to idx> <new col name> <mode replace add>Read the id,value from file and substitute the value in the dest file col idx Usage:
-break <label1> <label2> <columns(Column indices, one per line.Break apart column values and make new rows Usage:
Can include ranges, e.g. 0-5) >
-desc <column> <suffix>Add a description from wikipedia Usage:
-image <column> <suffix>Search for an image Usage:
-gender <column>Figure out the gender of the name in the column
-scale <column> <delta1> <scale> <delta2>Set value={value+delta1}*scale+delta2 Usage:
-rowaverage <>Average the row values Usage:
-generate <label> <start> <step>Add row values Usage:
-decimals <column> <how many decimals to round to>Usage:
-operator <columns(Column indices, one per line.Apply the operator to the given columns and create new one Usage:
Can include ranges, e.g. 0-5) > <new col name> <operator +,-,*,/>
-round <columns(Column indices, one per line.round the values Usage:
Can include ranges, e.g. 0-5) >
-sum <key columns> <value columns> <carry over columns>Sum values keying on name column value. If no value columns specified then do a count Usage:
-percent <columns to add>Usage:
-increase <column> <how far back>Calculate percent increase Usage:
-average <columns(Column indices, one per line.Calculate a moving average
Can include ranges, e.g. 0-5) > <period> <label>
-geocode <columns(Column indices, one per line.Usage:
Can include ranges, e.g. 0-5) > <prefix(e.g., state: or county:) > <suffix>
-geocodeaddressdb <columns(Column indices, one per line.Usage:
Can include ranges, e.g. 0-5) > <prefix> <suffix> >
-geocodejoin <column> <csv file(File to get lat/lon from) > <name idx> <lat idx> <lon idx>Geocode with file Usage:
-mercator <columns(Column indices, one per line.Convert x/y to lon/lat Usage:
Can include ranges, e.g. 0-5) >
-population <columns(Column indices, one per line.Add in population from address
Can include ranges, e.g. 0-5) > <prefix(e.g., state: or county:) > <suffix>
-sort <column(Column to sort on) >Usage:
-countShow count Usage:
-maxrows <Max rows to print>Usage:
-changeline <from> <to>Change the line Usage:
-changeraw <from> <to>Change input text Usage:
-strictBe strict on columns. any rows that are not the size of the other rows are dropped Usage:
-flagBe strict on columns. any rows that are not the size of the other rows are shown Usage:
-verify <# columns>Throw error if a row has a different number of columns Usage:
-comment <string>Usage:
-verifyVerify that all of the rows have the same # of columns
-delimiter <delimiter(Use 'space' for space, 'tab' for tab) >Specify a delimiter Usage:
-tabUse tabs Usage:
-widths <widths(w1,w2,...,wN) >Columns are fixed widths Usage:
-header <header(Column names) >Raw header Usage:
-html <skip(Number of tables to skip) > <pattern(Pattern to skip to) > <properties(Other attributes -Parse the table in the input html file Usage:
removeEntity false removePattern pattern) >
-htmlpattern <columns(Column names) > <startPattern> <endPattern> <pattern(Row pattern. Use (...) to match columns) >Parse the input html file Usage:
-json <arrayPath(Path to the array e.g., obj1.arr[2].obj2) > <objectPaths(One or more paths to the objects e.g. geometry,features) >Parse the input as json Usage:
-xml <path(Path to the elements) >Parse the input as xml Usage:
-text <comma separated header> <chunk pattern> <token pattern>Extract rows from the text Usage:
-tokenize <header(header1,header2...) > <pattern>Tokenize the input from the pattern Usage:
-prune <bytes(Number of leading bytes to remove) >Prune out the first N bytes
-template <prefix> <template(Use ${0},${1}, etc for values) > <delimiter(Output between rows) > <suffix>Apply the template to make the output Usage:
-rawPrint the file raw Usage:
-recordPrint records Usage:
-printheaderPrint the first line Usage:
-pointheaderGenerate the RAMADDA point properties Usage:
-addheader <properties(name1 value1 ... nameN valueN) >Add the RAMADDA point properties Usage:
-db <props(Name value pairs: table.idtable.name table.cansearch table.canlist table.icon .id .label .type .format .canlist .cansearch install nukedb Generate the RAMADDA db xml from the header Usage: -run <Name of process directory>Usage:-cat <*.csv>One or more csv files


No. We don't support ordering from the Taste app,
and we
don't take a cut if you call a restaurant from our app.
How can I easily find black owned restaurants?
You can find black
owned places on the search
page, see the video below:
Where are the receipts?
We are donating the final amounts at the end of the
campaign on July 12th. We will have the receipts up on our IG page and here by July 13th.
The restaurant I selected on Taste is not showing up as black owned, what should I
do? If we
haven't correctly marked a restaurant as black owned, at the bottom of the "Create Post" page
you will see a "Black Owned" tag. Just tap that and you're all set!
Why are you doing this?
Simple. a) We want to donate to organizations that fight
against racial injustice. We are a majority black founded start-up, so this cause is especially important for
us. b) We want to encourage you to support black owned establishments, especially during this
Coronavirus pandemic. c) We want you to come check out what we're building! :-)
Why are you committing only $10,000?
We want to commit more, but we don't have the
resources.
We were founded as a company just 8 months ago.
Got some other question for us? Shoot us an email at help@trytaste.app.


Module : websub
Module Overview
This module contains an implementation of the W3C WebSub recommendation, which facilitates a push-based content delivery/notification mechanism between publishers and subscribers.
This implementation supports introducing all WebSub components:
- Subscriber - A party interested in receiving update notifications for particular topics.
- Publisher - A party that advertises topics to which interested parties subscribe in order to receive notifications on occurrence of events.
- Hub - A party that accepts subscription requests from subscribers and delivers content to the subscribers when the topic is updated by the topic's publisher.
Basic flow with WebSub
-
The subscriber discovers from the publisher, the topic it needs to subscribe to and the hub(s) that deliver notifications on updates of the topic.
-
The subscriber sends a subscription request to one or more discovered hub(s) specifying the discovered topic along with other subscription parameters such as:
- The callback URL to which content is expected to be delivered.
- (Optional) The lease period (in seconds) the subscriber wants the subscription to stay active.
- (Optional) A secret to use for authenticated content distribution.
-
The hub sends an intent verification request to the specified callback URL. If the response indicates verification (by echoing a challenge specified in the request) by the subscriber, the subscription is added for the topic at the hub.
-
The publisher notifies the hub of updates to the topic and the content to deliver is identified.
-
The hub delivers the identified content to the subscribers of the topic.
Features
Subscriber
This module allows introducing a WebSub Subscriber Service with onIntentVerification
, which accepts HTTP GET requests for intent verification, and onNotification
, which accepts HTTP POST requests for notifications. The WebSub Subscriber Service provides the following capabilities:
- When the service is started a subscription request is sent for a hub/topic combination, either specified as annotations or discovered based on the resource URL specified as an annotation.
- If
onIntentVerification
is not specified, intent verification will be done automatically against the topic specified as an annotation or discovered based on the resource URL specified as an annotation. - If a secret is specified for the subscription, signature validation will be done for authenticated content distribution.
Sends subscription request on service startup and explicit intent verification
When the
subscribeOnStartUp
is set to true in the Subscriber Service, it will result in a subscription request being sent to the specified hub for the specified topic, with the specified lease seconds value and the specified secret for authenticated content distribution.
Since an
onIntentVerification
resource function is not included, intent verification for subscription and unsubscription requests would happen automatically.
ballerina @websub:SubscriberServiceConfig { path: "/websub", subscribeOnStartUp: true, target: ["<HUB_URL>", "<TOPIC_URL>"], leaseSeconds: 3600, secret: "<SECRET>" } service websubSubscriber on websubEP { resource function onNotification(websub:Notification notification) { //... } }
Explicit intent verification can be done by introducing an
onIntentVerification
resource function.
ballerina resource function onIntentVerification(websub:Caller caller, websub:IntentVerificationRequest request) { http:Response response = new; // Insert logic to build subscription/unsubscription intent verification response. error? result = caller->respond(response); }
Functions are made available on the websub:IntentVerificationRequest
to build a subscription or unsubscription
verification response, specifying the topic to verify intent against:
http:Response response = request.buildSubscriptionVerificationResponse("<TOPIC_TO_VERIFY_FOR>");
http:Response response = request.buildUnsubscriptionVerificationResponse("<TOPIC_TO_VERIFY_FOR>");
Hub
A WebSub compliant hub based on the Ballerina Message Broker is also available. This can be used as a remote hub or to be used by publishers who want to have their own internal hub. Ballerina's WebSub hub honors specified lease periods and supports authenticated content distribution.
Enabling Basic Auth support for the hub
The Ballerina WebSub Hub can be secured by enforcing authentication (Basic Authentication) and (optionally) authorization.
The AuthProvider
and authConfig
need to be specified for the hub listener and service respectively. If the
authStoreProvider
of the AuthProvider
is set as "http:CONFIG_AUTH_STORE", usernames and passwords for authentication and scopes for authorization would be read from a config TOML file.
A user can specify AuthProvider
as follows and set it to the hubListenerConfig
record, which is passed when starting the hub.
http:BasicAuthHandler basicAuthHandler = new(new auth:InboundBasicAuthProvider());
http:ServiceEndpointConfiguration hubListenerConfig = {
auth: {
authHandlers: [basicAuthHandler]
},
secureSocket: {
keyStore: {
path: config:getAsString("b7a.home") + "bre/security/ballerinaKeystore.p12",
password: "ballerina"
}
}
};
var val = websub:startHub(new http:Listener(9191, hubListenerConfig));
In addition to the BasicAuthHandler
for the listener, a user also has to specify the authConfig
properties at the service or resource levels.
They can be set by passing arguments for the serviceAuth
, subscriptionResourceAuth
or publisherResourceAuth
parameters when starting up the hub.
Recognized users can be specified in a .toml
file, which can be passed as a configuration file when running the program.
[b7a.users]
[b7a.users.tom]
password="1234"
scopes="scope1"
Once the hub is secured using basic auth, a subscriber should provide the relevant auth
config in the
hubClientConfig
field of the subscriber service annotation.
auth:OutboundBasicAuthProvider basicAuthProvider = new({
username: "tom",
password: "1234"
});
http:BasicAuthHandler basicAuthHandler = new(basicAuthProvider);
@websub:SubscriberServiceConfig {
path: "/ordereventsubscriber",
hubClientConfig: {
auth: {
authHandler: basicAuthHandler
}
}
}
Enabling data persistence for the hub
The Ballerina WebSub Hub supports persistence of topic and subscription data that needs to be restored when the hub is restarted.
Users can introduce their own persistence implementation, by introducing an object type that is structurally
equivalent to the websub:HubPersistenceStore
abstract object.
Persistence can be enabled by setting a suitable websub:HubPersistenceStore
value for the hubPersistenceStore
field
in the HubConfiguration
record, which is passed to the websub:startHub()
function.
Any subscriptions added at the hub will be available even after the hub is restarted.
Publisher
Ballerina WebSub publishers can use utility functions to add WebSub link headers indicating the hub and topic URLs, which facilitates WebSub discovery.
A hub client endpoint is also made available to publishers and subscribers to perform the following:
-
Publishers
-
Register a topic at the Hub
websub:PublisherClient websubHubClientEP = new ("http://localhost:9191/websub/publish"); error? registrationResponse = websubHubClientEP->registerTopic("http://websubpubtopic.com");
-
Publish to the hub indicating an update of the topic
websub:PublisherClient websubHubClientEP = new ("http://localhost:9191/websub/publish"); error? publishResponse = websubHubClientEP.publishUpdate("http://websubpubtopic.com", {"action": "publish", "mode": "internal-hub"});
-
-
Subscribers
-
Subscribe/Unsubscribe to/from topics at a hub
websub:SubscriptionClient websubHubClientEP = new("<HUB_URL>"); // Send subscription request for a subscriber service. websub:SubscriptionChangeRequest subscriptionRequest = { topic: "<TOPIC_URL>", callback: "<CALLBACK_URL>", secret: "<SECRET>" }; websub:SubscriptionChangeResponse|error subscriptionChangeResponse = websubHubClientEP->subscribe(subscriptionRequest); // Send unsubscription request for the subscriber service. websub:SubscriptionChangeRequest unsubscriptionRequest = { topic: "<TOPIC_URL>", callback: "<CALLBACK_URL>" }; websub:SubscriptionChangeResponse|error subscriptionChangeResponse = websubHubClientEP->unsubscribe(unsubscriptionRequest);
-
Introducing Specific Subscriber Services (Webhook Callback Services)
Ballerina's WebSub subscriber service listener can be extended to introduce specific Webhooks.
Instead of the single onNotification
resource, you can introduce multiple resources to accept content delivery requests using specific subscriber services. These resources will correspond to the content delivery requests that will
be delivered with respect to a particular topic.
For example, assume a scenario in which you receive notifications either when an issue is opened or when an issue is closed by subscribing to a particular topic in an issue tracking system. With a custom subscriber service listener, which extends the
generic WebSub subscriber service listener, you can allow two resources to accept content delivery requests (e.g., onIssueOpened
and onIssueClosed
) instead of the onNotification
resource.
These resources will accept two parameters:
- The generic
websub:Notification
record as the first parameter - A custom record corresponding to the expected (JSON) payload of the notification (e.g.,
IssueCreatedEvent
,IssueClosedEvent
)
You can introduce a specific service as such by extending the generic subscriber service listener, specifying a mapping between the expected notifications and the resources that requests need to be dispatched to.
The mapping can be based on one of the following indicators of a notification request. (Requests will then be dispatched based on the value of the indicator in the request and a pre-defined mapping.)
-
A request header
Dispatching will be based on the value of the request header specified as
topicHeader
.websub:ExtensionConfig extensionConfig = { topicIdentifier: websub:TOPIC_ID_HEADER, topicHeader: "<HEADER_TO_CONSIDER>", headerResourceMap: { "issueOpened": ["onIssueOpened", IssueOpenedEvent], "issueClosed": ["onIssueClosed", IssueClosedEvent] } };
The
"issueOpened": ["onIssueOpened", IssueOpenedEvent]
entry indicates that when the value of the<HEADER_TO_CONSIDER>
header isissueOpened
, dispatching should happen to a resource namedonIssueOpened
.The first parameter of this resource will be the generic
websub:Notification
record, and the second parameter will be a customIssueOpenedEvent
record mapping the JSON payload received when an issue is created. -
The payload: the value of a particular key in the JSON payload
Dispatching will be based on the value in the request payload of one of the map keys specified in the
payloadKeyResourceMap
map.websub:ExtensionConfig extensionConfig = { topicIdentifier: websub:TOPIC_ID_PAYLOAD_KEY, payloadKeyResourceMap: { "<PAYLOAD_KEY_TO_CONSIDER>": { "issueOpened": ["onIssueOpened", IssueOpenedEvent], "issueClosed": ["onIssueClosed", IssueClosedEvent] } } };
The
"issueOpened": ["onIssueOpened", IssueOpenedEvent]
entry indicates that when the value for the JSON payload key<PAYLOAD_KEY_TO_CONSIDER>
isissueOpened
, dispatching should happen to a resource namedonIssueOpened
.The first parameter of this resource will be the generic
websub:Notification
record, and the second parameter will be a customIssueOpenedEvent
record, mapping the JSON payload received when an issue is created. -
A request header and the payload (combination of the above two)
Dispatching will be based on both a request header and the payload as specified in the
headerAndPayloadKeyResourceMap
. Also, you can introduce aheaderResourceMap
and/or apayloadKeyResourceMap
as additional mappings.websub:ExtensionConfig extensionConfig = { topicIdentifier: websub:TOPIC_ID_HEADER_AND_PAYLOAD, topicHeader: "<HEADER_TO_CONSIDER>", headerAndPayloadKeyResourceMap: { "issue" : { "<PAYLOAD_KEY_TO_CONSIDER>" : { "opened": ["onIssueOpened", IssueOpenedEvent], "closed": ["onIssueClosed", IssueClosedEvent] } } } };
The
"opened": ["onIssueOpened", IssueOpenedEvent]
entry indicates that when the value of the<HEADER_TO_CONSIDER>
header isissue
and the value of the<PAYLOAD_KEY_TO_CONSIDER>
JSON payload key isopened
, dispatching should happen to a resource namedonIssueOpened
.The first parameter of this resource will be the generic
websub:Notification
record and the second parameter will be a customIssueOpenedEvent
record, mapping the JSON payload received when an issue is created.
The Specific Subscriber Service
In order to introduce a specific subscriber service, a new Ballerina listener
needs to be introduced. This listener
should wrap the generic ballerina/websub:Listener
and include the extension configuration described above.
The following example is for a service provider that
- allows registering webhooks to receive notifications when an issue is opened or assigned
- includes a header named "Event-Header" in each content delivery request indicating what event the notification is for (e.g., "onIssueOpened" when an issue is opened and "onIssueAssigned" when an issue is assigned)
import ballerina/lang.'object as objects;
import ballerina/websub;
// Introduce a record mapping the JSON payload received when an issue is opened.
public type IssueOpenedEvent record {
int id;
string title;
string openedBy;
};
// Introduce a record mapping the JSON payload received when an issue is assigned.
public type IssueAssignedEvent record {
int id;
string assignedTo;
};
// Introduce a new `listener` wrapping the generic `ballerina/websub:Listener`
public type WebhookListener object {
*objects:Listener;
private websub:Listener websubListener;
public function __init(int port) {
// Introduce the extension config, based on the mapping details.
websub:ExtensionConfig extensionConfig = {
topicIdentifier: websub:TOPIC_ID_HEADER,
topicHeader: "Event-Header",
headerResourceMap: {
"issueOpened": ["onIssueOpened", IssueOpenedEvent],
"issueAssigned": ["onIssueAssigned", IssueAssignedEvent]
}
};
// Set the extension config in the generic `websub:Listener` config.
websub:SubscriberListenerConfiguration sseConfig = {
extensionConfig: extensionConfig
};
// Initialize the wrapped generic listener.
self.websubListener = new(port, sseConfig);
}
public function __attach(service s, string? name = ()) returns error? {
return self.websubListener.__attach(s, name);
}
public function __start() returns error? {
return self.websubListener.__start();
}
public function __detach(service s) returns error? {
return self.websubListener.__detach(s);
}
public function __immediateStop() returns error? {
return self.websubListener.__immediateStop();
}
public function __gracefulStop() returns error? {
return self.websubListener.__gracefulStop();
}
};
A service can be introduced for the above service provider as follows.
import ballerina/io;
import ballerina/log;
import ballerina/websub;
@websub:SubscriberServiceConfig {
path: "/subscriber",
subscribeOnStartUp: false
}
service specificSubscriber on new WebhookListener(8080) {
resource function onIssueOpened(websub:Notification notification, IssueOpenedEvent issueOpened) {
log:printInfo(io:sprintf("Issue opened: ID: %s, Title: %s", issueOpened.id, issueOpened.title));
}
resource function onIssueAssigned(websub:Notification notification, IssueAssignedEvent issueAssigned) {
log:printInfo(io:sprintf("Issue ID %s assigned to %s", issueAssigned.id, issueAssigned.assignedTo));
}
}
For a step-by-step guide on introducing custom subscriber services, see the "Create Webhook Callback Services" section of "How to Extend Ballerina".
For information on the operations, which you can perform with this module, see the below Functions. For examples on the usage of the operations, see the following.
- Internal Hub Sample Example
- Remote Hub Sample Example
- Hub Client Sample Example
- Service Integration Sample Example
| Detail |
Holds the details of a WebSub error. |
| ExtensionConfig |
The extension configuration to introduce custom subscriber services. |
| HubConfiguration |
Record representing hub specific configurations. |
| HubStartedUpError |
Error to represent that a WebSubHub is already started up, encapsulating the started up Hub. |
| RemotePublishConfig |
Record representing remote publishing allowance. |
| SubscriberDetails |
Record to represent Subscriber Details. |
| SubscriberListenerConfiguration |
Represents the configuration for the WebSub Subscriber Service Listener. |
| SubscriberServiceConfiguration |
Configuration for a WebSubSubscriber service. |
| SubscriptionChangeRequest |
Record representing a WebSub subscription change request. |
| SubscriptionChangeResponse |
Record representing subscription/unsubscription details if a subscription/unsubscription request is successful. |
| SubscriptionDetails |
Record to represent persisted Subscription Details retrieved. |
| WebSubContent |
Record to represent a WebSub content delivery. |
| Hub |
Represents the Ballerina WebSub Hub. |
| HubPersistenceStore |
Represents the hub persistence configuration and functions. |
| IntentVerificationRequest |
Object representing an intent verification request received. |
| Notification |
Represents the WebSub Content Delivery Request received. |
| Caller |
The caller remote functions to respond to client requests. |
| PublisherClient |
The HTTP based client for WebSub topic registration and unregistration, and notifying the hub of new updates. |
| SubscriptionClient |
The HTTP based client for WebSub subscription and unsubscription. |
| Listener |
Represents the WebSubSubscriber Service Listener. |
| addWebSubLinkHeader |
Function to add link headers to a response to allow WebSub discovery. |
| extractTopicAndHubUrls |
Retrieves hub and topic URLs from the |
| startHub |
Starts up the Ballerina Hub. |
| WEBSUB_ERROR_CODE |
The constant used to represent error code of WebSub module. |
| PUBLISH_MODE_DIRECT |
|
| PUBLISH_MODE_FETCH |
|
| SHA1 |
The constant used to represent SHA-1 cryptographic hash algorithm |
| SHA256 |
The constant used to represent SHA-256 cryptographic hash algorithm |
| TOPIC_ID_HEADER |
|
| TOPIC_ID_PAYLOAD_KEY |
|
| TOPIC_ID_HEADER_AND_PAYLOAD |
|
| FAILURE_REASON_SUBSCRIPTION_GONE |
Represents subscription delivery failure due to the subscriber's callback URL replying with a HTTP 410 Gone status code. |
| FAILURE_REASON_FAILURE_STATUS_CODE |
Represents subscription delivery failures related to HTTP failure status codes except 410 Gone. |
| FAILURE_REASON_DELIVERY_FAILURE |
Represents subscription delivery failures related to network issues. |
| LISTENER_STARTUP_ERROR |
Represents the reason string for the |
| HUB_STARTUP_ERROR_REASON |
Represents the reason string for the |
| SpecificSubscriber |
Annotation to declare that the service represents a specific webhook. |
| SubscriberServiceConfig |
WebSub Subscriber Configuration for the service, indicating subscription related parameters. |
| FailureReason |
Represents subscription delivery failure reasons. |
| RemotePublishMode |
The identifier to be used to identify the mode in which update content should be identified. |
| SignatureMethod |
The identifier to be used to identify the cryptographic hash algorithm. |
| TopicIdentifier |
The identifier to be used to identify the topic for dispatching with custom subscriber services. |
| HubStartupError |
Represents a hub startup error. |
| ListenerStartupError |
Represents a listener startup error. |


never executed always true always false
1 {-# LANGUAGE Strict #-}
2
3 -- | A broker extracts a /key/ value from incoming messages
4 -- and creates, keeps and destroys a /resource/ for each key.
5 --
6 -- The demultiplexed messages and their resources are passed to
7 -- a custom 'MessageHandler'/
8 --
9 -- The user provides a 'Demultiplexer' is a pure function that
10 -- returns a key for the resource associated
11 -- to the message and potientially changes the
12 -- message.
13 --
14 -- The demultiplexer may also return a value indicating that
15 -- a new resource must be created, or that a message
16 -- shall be ignored.
17 --
18 -- The broker is run in a seperate process using 'async'.
19 -- The usual way to stop a broker is to 'cancel' it.
20 --
21 -- When cancelling a broker, the resource cleanup
22 -- actions for all resources will be called with
23 -- async exceptions masked.
24 --
25 -- In order to prevent the resource map filling up with
26 -- /dead/ resources, the user of this module has to ensure
27 -- that whenever a resource is not required anymore, a message
28 -- will be sent to the broker, that will cause the 'MessageHandler'
29 -- to be executed for the resource, which will in turn return,
30 -- return 'RemoveResource'.
31 module RIO.ProcessPool.Broker
32 ( spawnBroker,
33 BrokerConfig (..),
34 BrokerResult (..),
35 ResourceCreator,
36 Demultiplexer,
37 ResourceCleaner,
38 MessageHandler,
39 Multiplexed (..),
40 ResourceUpdate (..),
41 )
42 where
43
44 import qualified Data.Map.Strict as Map
45 import RIO
46 import UnliftIO.MessageBox
47 ( IsMessageBox (Input, newInput, receive),
48 IsMessageBoxArg (MessageBox, newMessageBox),
49 )
50 import Control.Concurrent.Async(AsyncCancelled)
51
52 -- | Spawn a broker with a new 'MessageBox',
53 -- and return its message 'Input' channel as well as
54 -- the 'Async' handle of the spawned process, needed to
55 -- stop the broker process.
56 --
57 -- * @k@ is the /key/ for the resource associated to an incoming
58 -- message
59 -- * @w'@ is the type of incoming messages.
60 -- * @w@ is the type of the demultiplexed messages.
61 -- * @a@ specifies the resource type.
62 -- * @m@ is the base monad
63 spawnBroker ::
64 forall brokerBoxArg k w' w a m.
65 ( HasLogFunc m,
66 Ord k,
67 Display k,
68 IsMessageBoxArg brokerBoxArg
69 ) =>
70 brokerBoxArg ->
71 BrokerConfig k w' w a m ->
72 RIO
73 m
74 ( Either
75 SomeException
76 ( Input (MessageBox brokerBoxArg) w',
77 Async BrokerResult
78 )
79 )
80 spawnBroker brokerBoxArg config = do
81 brokerA <- async $ do
82 mBrokerBox <-
83 tryAny
84 ( do
85 b <- newMessageBox brokerBoxArg
86 i <- newInput b
87 return (b, i)
88 )
89 case mBrokerBox of
90 Left er -> return (Left er)
91 Right (brokerBox, brokerInp) -> do
92 aInner <- mask_ $
93 asyncWithUnmask $ \unmaskInner ->
94 brokerLoop unmaskInner brokerBox config Map.empty
95 return (Right (brokerInp, aInner))
96 join <$> waitCatch brokerA
97
98 -- | This is just what the 'Async' returned from
99 -- 'spawnBroker' returns, it's current purpose is to
100 -- make code easier to read.
101 --
102 -- Instead of some @Async ()@ that could be anything,
103 -- there is @Async<EMAIL_ADDRESS>104 data BrokerResult = MkBrokerResult
105 deriving stock (Show, Eq)
106
107 -- | The broker configuration, used by 'spawnBroker'.
108 --
109 -- * @k@ is the /key/ for the resource associated to an incoming
110 -- message
111 -- * @w'@ is the type of incoming messages.
112 -- * @w@ is the type of the demultiplexed messages.
113 -- * @a@ specifies the resource type.
114 -- * @m@ is the base monad
115 data BrokerConfig k w' w a m = MkBrokerConfig
116 { demultiplexer :: !(Demultiplexer w' k w),
117 messageDispatcher :: !(MessageHandler k w a m),
118 resourceCreator :: !(ResourceCreator k w a m),
119 resourceCleaner :: !(ResourceCleaner k a m)
120 }
121
122 -- | User supplied callback to extract the key and the 'Multiplexed'
123 -- from a message.
124 -- (Sync-) Exceptions thrown from this function are caught and lead
125 -- to dropping of the incoming message, while the broker continues.
126 --
127 -- * @k@ is the /key/ for the resource associated to an incoming
128 -- message
129 -- * @w'@ is the type of incoming messages.
130 -- * @w@ is the type of the demultiplexed messages.
131 type Demultiplexer w' k w = w' -> Multiplexed k w
132
133 -- | User supplied callback to use the 'Multiplexed' message and
134 -- the associated resource.
135 -- (Sync-) Exceptions thrown from this function are caught and lead
136 -- to immediate cleanup of the resource but the broker continues.
137 --
138 -- * Type @k@ is the /key/ for the resource associated to an incoming
139 -- message
140 -- * Type @w@ is the type of incoming, demultiplexed, messages.
141 -- * Type @a@ specifies the resource type.
142 -- * Type @m@ is the base monad
143 type MessageHandler k w a m = k -> w -> a -> RIO m (ResourceUpdate a)
144
145 -- | This value indicates in what state a worker is in after the
146 -- 'MessageHandler' action was executed.
147 data ResourceUpdate a
148 = -- | The resources is still required.
149 KeepResource
150 | -- | The resource is still required but must be updated.
151 UpdateResource a
152 | -- | The resource is obsolete and can
153 -- be removed from the broker.
154 -- The broker will call 'ResourceCleaner' either
155 -- on the current, or an updated resource value.
156 RemoveResource !(Maybe a)
157
158 -- | The action that the broker has to take for in incoming message.
159 --
160 -- * @k@ is the /key/ for the resource associated to an incoming
161 -- message
162 -- * @w@ is the type of the demultiplexed messages.
163 data Multiplexed k w
164 = -- | The message is an initialization message, that requires the
165 -- creation of a new resouce for the given key.
166 -- When the resource is created, then /maybe/ additionally
167 -- a message will also be dispatched.
168 Initialize k !(Maybe w)
169 | -- | Dispatch a message using an existing resource.
170 -- Silently ignore if no resource for the key exists.
171 Dispatch k w
172
173 -- deriving stock (Show)
174
175 -- | User supplied callback to create and initialize a resource.
176 -- (Sync-) Exceptions thrown from this function are caught,
177 -- and the broker continues.
178 --
179 -- * @k@ is the /key/ for the resource associated to an incoming
180 -- message
181 -- * @w@ is the type of the demultiplexed messages.
182 -- * @a@ specifies the resource type.
183 -- * @m@ is the monad of the returned action.
184 type ResourceCreator k w a m = k -> Maybe w -> RIO m a
185
186 -- | User supplied callback called _with exceptions masked_
187 -- when the 'MessageHandler' returns 'RemoveResource'
188 -- (Sync-) Exceptions thrown from this function are caught,
189 -- and do not prevent the removal of the resource, also the
190 -- broker continues.
191 --
192 -- * @k@ is the /key/ for the resource associated to an incoming
193 -- message
194 -- * @a@ specifies the resource type.
195 -- * @m@ is the monad of the returned action.
196 type ResourceCleaner k a m = k -> a -> RIO m ()
197
198 type BrokerState k a = Map k a
199
200 {-# NOINLINE brokerLoop #-}
201 brokerLoop ::
202 ( HasLogFunc m,
203 Ord k,
204 Display k,
205 IsMessageBox msgBox
206 ) =>
207 (forall x. RIO m x -> RIO m x) ->
208 msgBox w' ->
209 BrokerConfig k w' w a m ->
210 BrokerState k a ->
211 RIO m BrokerResult
212 brokerLoop unmask brokerBox config brokerState =
213 withException
214 ( unmask (receive brokerBox)
215 >>= traverse (tryAny . onIncoming unmask config brokerState)
216 )
217 ( \(ex :: SomeException) -> do
218 case fromException ex of
219 Just (_cancelled :: AsyncCancelled) ->
220 logDebug "broker loop: cancelled"
221 _ ->
222 logError
223 ( "broker loop: exception while \
224 \receiving and dispatching messages: "
225 <> display ex
226 )
227 cleanupAllResources config brokerState
228 )
229 >>= maybe
230 ( do
231 logError "broker loop: failed to receive next message"
232 cleanupAllResources config brokerState
233 return MkBrokerResult
234 )
235 ( \res -> do
236 next <-
237 either
238 ( \err -> do
239 logWarn
240 ( "broker loop: Handling the last message\
241 \ caused an exception:"
242 <> display err
243 )
244 return brokerState
245 )
246 return
247 res
248 brokerLoop unmask brokerBox config next
249 )
250
251 {-# NOINLINE onIncoming #-}
252 onIncoming ::
253 (Ord k, HasLogFunc m, Display k) =>
254 (forall x. RIO m x -> RIO m x) ->
255 BrokerConfig k w' w a m ->
256 BrokerState k a ->
257 w' ->
258 RIO m (BrokerState k a)
259 onIncoming unmask config brokerState w' =
260 case demultiplexer config w' of
261 Initialize k mw ->
262 onInitialize unmask k config brokerState mw
263 Dispatch k w ->
264 onDispatch unmask k w config brokerState
265
266 onInitialize ::
267 (Ord k, HasLogFunc m, Display k) =>
268 (forall x. RIO m x -> RIO m x) ->
269 k ->
270 BrokerConfig k w' w a m ->
271 BrokerState k a ->
272 Maybe w ->
273 RIO m (BrokerState k a)
274 onInitialize unmask k config brokerState mw =
275 case Map.lookup k brokerState of
276 Just _ -> do
277 logError
278 ( "cannot initialize a new worker, a worker with that ID exists: "
279 <> display k
280 )
281 return brokerState
282 Nothing ->
283 tryAny (unmask (resourceCreator config k mw))
284 >>= either
285 ( \err -> do
286 logError
287 ( "the resource creator for worker "
288 <> display k
289 <> " threw an exception: "
290 <> display err
291 )
292 return brokerState
293 )
294 ( \res ->
295 let brokerState1 = Map.insert k res brokerState
296 in case mw of
297 Nothing ->
298 return brokerState1
299 Just w ->
300 onException
301 (onDispatch unmask k w config brokerState1)
302 ( do
303 logError
304 ( "exception while dispatching the "
305 <> "post-initialization message for worker: "
306 <> display k
307 )
308 resourceCleaner config k res
309 )
310 )
311
312 onDispatch ::
313 (Ord k, HasLogFunc m, Display k) =>
314 (forall x. RIO m x -> RIO m x) ->
315 k ->
316 w ->
317 BrokerConfig k w' w a m ->
318 BrokerState k a ->
319 RIO m (BrokerState k a)
320 onDispatch unmask k w config brokerState =
321 maybe notFound dispatch (Map.lookup k brokerState)
322 where
323 notFound = do
324 logWarn
325 ( "cannot dispatch message, worker not found: "
326 <> display k
327 )
328 return brokerState
329 dispatch res =
330 tryAny (unmask (messageDispatcher config k w res))
331 >>= either
332 ( \err -> do
333
334 logError
335 ( "the message dispatcher callback for worker "
336 <> display k
337 <> " threw: "
338 <> display err
339 )
340 cleanupResource
341 k
342 config
343 brokerState
344 )
345 ( \case
346 KeepResource ->
347 return brokerState
348 UpdateResource newRes ->
349 return (Map.insert k newRes brokerState)
350 RemoveResource mNewRes ->
351 cleanupResource
352 k
353 config
354 ( maybe
355 brokerState
356 ( \newRes ->
357 Map.insert k newRes brokerState
358 )
359 mNewRes
360 )
361 )
362
363 cleanupAllResources ::
364 BrokerConfig k w' w a m ->
365 BrokerState k a ->
366 RIO m ()
367 cleanupAllResources config brokerState =
368 traverse_
369 ( uncurry
370 (tryResourceCleaner config)
371 )
372 (Map.assocs brokerState)
373
374 cleanupResource ::
375 (Ord k) =>
376 k ->
377 BrokerConfig k w' w a m ->
378 Map k a ->
379 RIO m (Map k a)
380 cleanupResource k config brokerState = do
381 traverse_ (tryResourceCleaner config k) (Map.lookup k brokerState)
382 return (Map.delete k brokerState)
383
384 tryResourceCleaner ::
385 BrokerConfig k w' w a m ->
386 k ->
387 a ->
388 RIO m ()
389 tryResourceCleaner config k res = do
390 void $ tryAny (resourceCleaner config k res)


My contact information
Tlf: +34 xx xxx xxx
email:
email me
Name
E-mail
Do you accept the terms and conditions


Duration
25 minutes
Goals
This exercise will modify an existing application that is using an ImageCell
and replace it with a custom ViewCell
template which displays additional information and provides some formatting.
Required Assets
There is a starter application in the Part 01 Resources folder. This is the project you will work with throughout most of this class. There is also a completed version of this exercise in the same folder for you to compare your work against.
Challenge
This exercise replaces an ImageCell
definition with a ViewCell
definition, allowing you to reposition the content, change the font sizes and add in new visuals not available in the stock cell types. You will define a cell style which looks like:
Here are the steps you will go through:
-
Replace the existing
ImageCell
with aViewCell
. -
You will want to keep the current
MenuItem
added to theContextActions
collection on theImageCell
. -
Add a
Grid
into theViewCell
with 4 columns:- 75px to hold an image.
- Star-sized for the Name and Email.
- Auto-sized for a "Favorites" star image.
- 5px for a padding on the right side.
-
Display the
HeadshotUrl
with anImage
in the first column.-
You will need the same value converter used in the current
ImageCell
to handle the embedded resource. - The size should be set to 75x75 so they image always fits the area.
- Set the
Aspect
property to "AspectFit".
-
You will need the same value converter used in the current
-
Use a
StackLayout
in the second column to display twoLabel
s for theName
andEmail
properties.- The name should have a Bold, Medium-sized font.
- The email should have a Small font.
-
Next, add a
Image
into the third column with theIsVisible
property data bound to theIsFavorite
property so it shows only when the character is marked as a favorite.- It should be centered vertically and placed on the right side horizontally.
- The image itself is already in each of the platform-specific projects, so just use "star.png" as the
ImageSource
.
-
Finally, add a
Label
to display how old the character is according to theDob
property. This will require a newIValueConverter
to calculate the age. It should be placed in the second column and marked to span columns two and three and positioned on the right side (under the image) as you can see from the screen shot.
Try to complete the exercise using the above screenshot and instructions, and then use the detailed information below if you get stuck or need some additional information to complete the step.
Steps
Replace the ImageCell
with a ViewCell
In this first step, we will remove the ImageCell
used in the last exercise and replace it with a ViewCell
which allows us to define the row visualization however we like. We will start with the headshot, name and email.
-
Open the AllContacts.xaml markup file and locate the
ImageCell
inside theDataTemplate
definition. -
Copy the Binding used for the
ImageSource
property - you will need that in a moment. -
Also, copy off the
ImageCell.ContextActions
property settings - it should have a singleMenuItem
assigned to it. This provides the swip to delete UI which we will want in our new cell as well. - Replace the entire
ImageCell
with aViewCell
definition. - Inside the
ViewCell
, add aGrid
and add three columns to it:<ColumnDefinition Width="75" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
- In the first column, add an
Image
and data bind theSource
property to theHeadshotUrl
property of thePerson
.- Set the
WidthRequest
andHeightRequest
properties to "75". - Set the
Aspect
property to "AspectFit" so the image looks correct. - Center the image vertically through the
VerticalOptions
property.
- Set the
- In the second column, add a
StackLayout
. - In the
StackLayout
, add aLabel
to display the name.- Bind the
Text
property to theName
property. - Set the
FontSize
property to "Medium" - Set the
FontAttributes
property to "Bold"
- Bind the
- Add a second
Label
to theStackLayout
for the email address.- Bind the
Text
property to theEmail
property. - Set the
FontSize
property to "Small" - Set the
TextColor
property to "Accent" to get a reasonable secondary color.
- Bind the
-
Finally, add the
ContextActions
onto theViewCell
-
Run the application to see the results. Xamarin.Forms will auto-size all the rows to the largest row in the data set. But it will look a bit squished, so set the
RowHeight
on theListView
to be "80" to give it a little more space. -
Set the
VerticalOptions
on theStackLayout
to "Center" so the text content is centered within the cell.
Add dynamic content to the ViewCell
In this next step, we are going to display a "favorites" icon next to each of the characters that have the IsFavorite
property set. We will use an existing icon that's already in the platform-specific projects - star.png.
- Add an
Image
to the third column in theGrid
- Set the
Source
property to "star.png" - Set the
Grid.Column
property to "2" - Set the
VerticalOptions
to "Center" - Set the
HorizontalOptions
to "End" to right-align the icon. - Databind the
IsFavorite
property to the image'sIsVisible
property. Since they are bothbool
types, there is no conversion necessary.
- Set the
- Run the application and verify that the icon is displayed on the right side when the character is marked as a favorite. Try changing the flag by editing the details and navigating back to the list.
Add an extra field for Age
In this final step, we'll add a new Label
to display how old the character is. We will need to write a Value Converter to take the Person.Dob
property and turn it into an age.
- Add a
Label
after theImage
.- Set the
Grid.Column
to "1" and theGrid.ColumnSpan
to "2" so the label spans the final two columns. - Set the
HorizontalOptions
to "End" so it's positioned at the right edge. - Set the
VerticalOptions
to "End" to it's positioned at the bottom. - Set the
FontSize
to "Micro to make it smaller than the email address. - Set the
TextColor
to "Accent" so it matches the email address. - Finally, data bind the
Text
property to thePerson.Dob
property.
- Set the
- Next, since the
Dob
is not astring
, and is not the age we want, we will need a converter to get the correct data. Add a new source file to the Converters folder named BirthdayConverter. - Implement the
IValueConverter
interface - you will only need to provide an implementation for theConvert
method since we won't be using this in a two-way binding. - Copy the logic below to take the
DateTime
input and turn it into an age in years (integer).- The inbound
value
parameter will be aDateTime
. - You should return the age as an integer.
- The inbound
public class BirthdayConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { DateTime bday = (DateTime)value; DateTime today = DateTime.Today; int age = today.Year - bday.Year; return (bday > today.AddYears(-age)) ? age-1 : age; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
- Add an instance of
BirthdayConverter
into yourResourceDictionary
. - Apply the converter to your
Label
binding. Use theStringFormat
property of the Binding to display the returned integer with the suffix "yrs. old".
- Run the application and verify it shows the age of the character in the bottom right of the cell. It should look something like the following screen:
Summary
In this exercise, we customized a ListView
using a DataTemplate
and data binding. This is incredibly easy to take advantage of. Keep in mind there are built-in cells you can use, and if your design fits one of these, you should prefer to use them because they are slightly more efficient than the ViewCell
alternative.


In writing the resources chapter of my WinForms book, I found that there was no single utility for displaying the resource contents of assemblies, .resx files and .resource files (even embedded .resources files) in a way that made sense to me. So, I built one and called it ResourceExplorer.
ResourceExplorer is very simple and could stand some extension to show specific resources at images, text, data, etc, but it certainly served my needs, especially when it came to understanding the usage and limitations of the ResourceReader (for reading .resources files) and the ResXResourceReader (for reading .resx files). Enjoy.


THE MAN WHO KNEW TOO LITTLE
BAD!
Every year there are some bad movies but they usually involve non-stars. That Bill Murray should be in a film that is so bad is pitiful. It is not alone the story; it is Murray himself who has grown chubby in the face, looks dissipated but still tries to exude the boyish charm and glib speech for which he has been noted lo these many years. You cannot keep doing that; it palls.
The story reeks of contrivance. Wallace (Bill Murray) is a video store clerk from Iowa. Flying to London to celebrate his birthday with his wealthy brother James (Peter Gallagher) he is treated to a surprise by James who takes him to a "Theater of Life" where the audience gets into the act and takes on a role. Wallace takes it seriously and is soon unable to distinguish the participatory part of the theater with real life. Not very funny.
When Wallace takes a phone call, it is a wrong number call which should have gone to government-hired assassins who wants to overthrow the current British government and start another cold war with Russia. Wallace becomes involved with this assassination group. He thinks it is part of the acting he is doing. Are you still with me? This is as far as I will go.
Joanne Walley is wasted in the film.
It took two writers to write this mess of a screenplay: Robert Farrar and Howard Franklin.
It took only one director: Jon Ami
0 Bytes
4 bytes = Superb 3 bytes = Too good to miss 2 bytes = Average 1 byte = Save your money
Copyright 1997 Ben Hoffman
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


Constructor
new MASPluginMQTTClient(clientId, masMQTTConstants)
Parameters:
| Name |
Type |
Description |
clientId |
string
|
Unique broker-client ID for this client. Default clientId is the one picked from msso_config.json |
masMQTTConstants |
MASPluginMQTTConstants
|
An instance of populated MASPluginMQTTConstants object. |
- Source:
Methods
connect(successHandler, errorHandler)
Connects to a message broker using the MASPluginMQTTConstants that is provided during construction of this object.
Parameters:
| Name |
Type |
Description |
successHandler |
successCallbackFunction
|
user defined success callback that is invoked on success scenario. |
errorHandler |
errorCallbackFunction
|
user defined error callback that is invoked on failure scenario. |
- Source:
disconnect(successHandler, errorHandler)
Disconnects from the connected message broker
Parameters:
| Name |
Type |
Description |
successHandler |
successCallbackFunction
|
user defined success callback that is invoked on success scenario. |
errorHandler |
errorCallbackFunction
|
user defined error callback that is invoked on failure scenario. |
- Source:
isConnected(successHandler, errorHandler)
Checks if device is connected to the MQTT broker.
Parameters:
| Name |
Type |
Description |
successHandler |
successCallbackFunction
|
user defined success callback that is invoked on success scenario. |
errorHandler |
errorCallbackFunction
|
user defined error callback that is invoked on failure scenario. |
- Source:
publish(successHandler, errorHandler, topicName, message, QoS, retain)
Publish to a topic via the connected MQTT broker.
Parameters:
| Name |
Type |
Description |
successHandler |
successCallbackFunction
|
user defined success callback that is invoked on success scenario. |
errorHandler |
errorCallbackFunction
|
user defined error callback that is invoked on failure scenario. |
topicName |
string
|
topic to which the user sends the message |
message |
string
|
specifies the message to be sent. The message bytes should be a base64 encoded string to support sending images |
QoS |
integer
|
Quality of Service for message delivery.0-> At most once,1-> At least once, 2: Exactly once. Default is 2 |
retain |
boolean
|
indication for the broker to persist the messages for a client |
- Source:
subscribe(successHandler, errorHandler, topicName, QoS)
Subscribes to a topic via the connected MQTT broker.
Parameters:
- Source:
unsubscribe(successHandler, errorHandler, topicName)
Unsubscribe from a topic
Parameters:
| Name |
Type |
Description |
successHandler |
successCallbackFunction
|
user defined success callback that is invoked on success scenario. |
errorHandler |
errorCallbackFunction
|
user defined error callback that is invoked on failure scenario. |
topicName |
string
|
topic to which the user needs to unsubscribe. |
- Source:


| ev4_subm | ||
| open | /~ashipunova/illumina_submission/index.php | |
| clickAndWait | link=Upload Submission Metadata | |
| type | id=csv | /Users/ashipunova/work/bpc/illumina_submission/selenium_tests/bslikas_193453_VAMPS_submission_a.csv |
| clickAndWait | name=Submit | |
| clickAndWait | id=run_info_form_add | |
| clickAndWait | id=form_update_from_csv | |
| assertText | css=div.green_message | ../illumina_submission_site_local_add/miseq_info/20150121/metadata_20150121_1_E.csv was created |


"The Key is falling in love with something, anything. If your heart's attached to it, then your mind will be attached to it." - Vera Wang
"Some people look for a beautiful place. Others make a place beautiful.”
- Hazrat Inavat Khan
“Whatever you do, do it well. Do it so well that when people see you do it they will want to come back and see you do it again and they will want to bring others and show them how well you do what you do.” - Walt Disney
My name is Diane Dubray, I am (Oglala Lakota) from the Pine Ridge Reservation. I am a mother of three children whom I love to the moon and back ☾ I am also considered a road runner, my heart belongs to the highway... I love to travel and grab the opportunity at any chance to spin my wheels and get to a new destination. People would say that I am a social butterfly, I am very enthusiastic, optimistic, and I am a go getter. One of my greatest strengths is working best under pressure or with little notice to assist in booking venues. I organized my first event in 2013 when I was asked to help organize an event just weeks prior to the Inauguration, November 2012. I have helped many Native American artists throughout the years by helping them to connect, find gigs, have meet-ups etc.. From experience, I've learned that this is something I've become very passionate and interested in, I would love to offer my time to those who are looking for assitance in collaborating for a gathering and finding their next destination. Just as the road has done for me.
If you have any questions, please do not hesitate to contact me directly. I will get back to you as soon as possible.


First name:
Last name:
Busch Student Center
Cook Student Center
College Avenue Student Center
Douglass Student Center
Livingston Student Center
Brower Commons
Busch Dining Hall
Neilson Dining Hall
Livingston Dining Commons


Contains implementation of connection parameters, using various connection strings,
which are stripped of all credentials. If we need to configure a service, the port,
ip address, protocol, and other parameters – we use the ConnectionParams object, and
relevant helper classes (like ConnectionResolver), for acquiring these parameters,
and for discovery of objects, components (which store and retrieve connection parameters).
Discovery
Service that store a registry of various end-points (what services are where, and how to
connect to them). It knows the end-points, but doesn't have the credentials to connect to them.
Separated for security reasons.
Static discovery: all services have static IP addresses (like DNS, which also works using static
discovery) that are configured from the start and don't change along the way. As of lately, used
more often than dynamic, because it is simpler to use and more reliable.
Proxy (or reverse proxy) is created with a dns name, and all the dynamics of
starting/restarting/switching from one host to another – everything is nice and clear
for the clients. Infrastructure does all the hard work out of the box.
Configure sets the static registry.
Dynamic discovery: every time a service starts, it registers its address in the discovery
service ("Service name" at the following address "IP"). Clients then ask to resolve the address
by which the requested service can be reached. The service has a general name, by which other
services can resolve it.
If a service stops working, you need to refresh its address, clean stale addresses,
heartbeats must be used – lots of problems and challenges.
Contains implementation of connection parameters, using various connection strings, which are stripped of all credentials. If we need to configure a service, the port, ip address, protocol, and other parameters – we use the ConnectionParams object, and relevant helper classes (like ConnectionResolver), for acquiring these parameters, and for discovery of objects, components (which store and retrieve connection parameters).
Discovery
Service that store a registry of various end-points (what services are where, and how to connect to them). It knows the end-points, but doesn't have the credentials to connect to them. Separated for security reasons.
IDiscovery – interface for creating registries.
MemoryDiscovery – registry that is stored in memory.
There exist 2 types of discovery:
Static discovery: all services have static IP addresses (like DNS, which also works using static discovery) that are configured from the start and don't change along the way. As of lately, used more often than dynamic, because it is simpler to use and more reliable.
Dynamic discovery: every time a service starts, it registers its address in the discovery service ("Service name" at the following address "IP"). Clients then ask to resolve the address by which the requested service can be reached. The service has a general name, by which other services can resolve it.
One service can have more than one address.


Indicates the intended audience for the element. This is a property attribute which supports conditional processing for filtering or flagging. If no value is specified, but the attribute is specified on an ancestor within a map or within the related-links section, the value will cascade from the closest ancestor. The attribute takes a space-delimited set of values.