Ask questions or make suggestions about marketing, business development, or anything else relevant. This is the channel to discuss topics with Komodo team.
Which of these do you think is @KomodoPlatform's Crown Jewel? Do you think its atomic swap powered DEX/dICOs? Federated multichain MoMoM scaling? dPoW securing scaling chains & 3rd party coins? or Zk-snarks & 5.1% rewards? or comment what you think it is below.
#kmd #btc ...
5
infotech-al31-Jul-18 05:21 PM
Just tried to download CRED app from Apple Store @benohanlon its a no-go
KomodoWorld31-Jul-18 07:17 PM
@infotech-al reading further I found it launches tomorrow in 4 US states. The PR was not completely clear on this
2
Regnar31-Jul-18 07:24 PM
Yea on the site it has the states it's available in, soft roll out is expected tbh. Even Robinhood is doing it.(edited)
passcombo.com 🏴☠🏴31-Jul-18 09:50 PM
do we have somewhere official link to atomic explorer ? couldnt find on komodoplatform.com
Kay Gee31-Jul-18 10:21 PM
@Mylo one of my circle mentioned a tweet from this week of a KMD credit card. Had a lot of traction over here. In terms of simplicity (making the tech easy to adopt and easy to use), that was massive. Copy should sell brand, CTAs should hit and highlight deliverables.
Which of these do you think is @KomodoPlatform's Crown Jewel? Do you think its atomic swap powered DEX/dICOs? Federated multichain MoMoM scaling? dPoW securing scaling chains & 3rd party coins? or Zk-snarks & 5.1% rewards? or comment what you think it is below.
#kmd #btc ...
Mylo01-Aug-18 12:41 AM
atomicexplorer.com ?
Not official. Yes, good point. These are things that marketing and the agency have on the table. There is a some unity required, and 1 or 2 community sites will 99.9% be voted to receive some branding love as well as better resource linking.
"We are all involved in the education aspect, it's part of our social contract to leave something for other people to learn from" (paraphrased badly) was what was said to me in the first meeting. This is another part we are working on, a "learn more" area for traders and developers.
peer2krd and notary involvement with the world/community/side-projects as well. There are non-english speaking forums where out of all popular equihash coins, kmd is the lowest mentioned.
Has anyone used this peer2krd? Has anyone ordered? @Virginia (Peer2Group) how many batches/customers can you guys support - i read in one channel the first batch of cards is out and waiting for next batch - which EU countries etc. need more info. Can you work with community #komodo-team - i had a quick look at kmd twitter and a message was retweeted once or twice in mid July. Do you want more ppl to sign up, can you work with this channel first for feedback and i'll see what our writing resources are like for an update. we recently wrote about a partnership with gonetcoins which has a service in canada entering the US with reloads available in many stores via virtual ATM.
@Regnar nice twitter poll thanks for getting the info, it is quite interesting.
@Kay Gee sell brand to who?
I'd like to re-visit the twitter poll concept.
Anyhow, I've got some dev work to do. My first exposure to smart contracts was to test a blockchain enforced contract for a faucet. This is in alpha testing, history is in #cc-general channel. https://github.com/imylomylo/KMD-CC-Token-Test-Guide/tree/master/rpc/faucet
Unfortunately i ran into a conflict with a docker image for testing contracts. Docker is a technology where you can basically "press play" and you have a service. It needs some further work but makes the dev-experience for non-system (e.g. front end) & other tech users quite easy to get involved. https://github.com/imylomylo/docker-komodod is the start, it needs some docs and maybe a simple dev front end so community can get involved during alpha phase.
There is also lightning network testing being done with a side project, which integrates with smart contracts - hopefully include community too!!
Organize anything, together. Trello is a collaboration tool that organizes your projects into boards. In one glance, know what's being worked on, who's working on what, and where something is in a process.
Add questions / requests / ideas so we can discuss on Monday meeting.
Kay Gee01-Aug-18 06:19 AM
@Mylo sorry, I’ve worked in advertising for years and I fall back to default positions. Imo copy should always sell the brand to the reader, all readers- that’s why there is copy. Surely that is why there is a website, right? To inform and involve KMD users and to grow the KMD user base? Selling the brand just means explaining why KMD over ETH, for example.
On each page, in each block of text, you want to trigger an emotional response - whether that is pathos or desire is largely irrelevant, but the stronger the emotion evoked, the greater the likelihood you will create a behavioral change. What I was clumsily saying is that short copy can sell the big picture concepts (simplicity, security, flexibility, etc.) but the CTAs don’t just have to be “[we’re the brand/platform/ for you]” as there are deliverables which in and of themselves are seemingly creating hype - without any context.
Anyways, I’m sure you know this and I’m just riffing because of excitement: for once, this is a project I’m invested in (quite literally) rather than one I’m being paid to care about.
There may also be an element of advanced procrastination at play
A native #blockchain in 5 minutes; full customization; countless possibilities – listen the Komodo Pioneers #Podcast and learn more.
https://t.co/zyGD4FxNAN
$KMD #Komodo #cryptocurrency @KomdoPlatform
Big thanks to @ejuliano@Alright@Mylo
Mylo01-Aug-18 08:22 AM
Yeeeeewwwww!
@Kay Gee i'm a dev by trade, but worked with more bad marketers than good, so only know enough to be dangerous. evoking emotions that's advertiser's craft, but yeah marketers (the good and bad) make a living on those emotions. CTAs and all the copy are being updated - there was a message testing post on reddit in the past few days and we're working with an agency to unlock what we have already, where we'll be in jan 2019 with the new tech, which is super engaging - interoperable, simple to make apps on with a very rich feature set.
Virginia (Peer2Group)01-Aug-18 09:18 AM
Hi @Mylo Mylo,
First of all, I want to thank you for the support.
This 1st batch was 100 cards. For the moment, the cards are only eligible for legal EU residents, that is, citizens who are legally living in a european country which belong to Eurozone.
Your proposal sounds very good! I'm going to contact you by another channel, for more details. Thanks!
passcombo.com 🏴☠🏴01-Aug-18 09:49 AM
@Virginia (Peer2Group)@Mylo is that also possible to add topup of other KMD platform coins like Hush, BTCH or Chips? I saw currently it's BTC and KMD, which is good enough, but possibility of other coins (not assets) would be good? I also didn't see more detailes on the page (from where the price it taken and how much % the provider takes. Also good to display team members details (linkein?) for more trust then just anon page. Then I think KMD card could compete with other popular ??
Actually to topup also other popular coins like LTC/ETH/XMR/Dash could be good from marketing perspective - other communities may like to have it.
also for better marketing if pre-ann correctly (widely) after doing above adjustment some time limit could be made to subscribe for "free card" and after that period cards could be like 10$ or so (or lower % charge margin for early birds) ? Thanks to this hurry-up effect more people could join this quickly and could be more viral...
For example the charge could be 1% for early birds and 3-5% afterwards (not sure what is it now, but should compete with Monaco and TenX)
And also calculate the cost of card production per unit - later it might be charged that amount.
The Dubai court system tasked to oversee all civil and commercial disputes involving financial transactions both domestically and internationally is turning to blockchain technology to “create the world’s first disruptive court”. The Dubai International Financial Center...
KomodoWorld01-Aug-18 01:25 PM
ah no I'm still banned from r/cryptocurrency, in the new ui I see the button and only when I try to post it says I can't
passcombo.com 🏴☠🏴01-Aug-18 01:56 PM
decentralized forum is needed :p
kimmel (Pungo.io)01-Aug-18 02:01 PM
@passcombo.com 🏴☠🏴 thanks for the great suggestions and ideas. This is just a 1st step phase thats why 100 cards. Of course we can accept many other cryptos, but as you see we are already sold out the cards in about 4 days and have quite a big waiting list. We will work on it during next months and I don't discard a 2nd, bigger batch in september
passcombo.com 🏴☠🏴01-Aug-18 02:10 PM
so this is also maybe a reason to have these cards paid sth - to hire more people as there is probably some manual work involved in the process ? question if you got already a business model to make a proper pricelist or overall action plan. If there is so many people waiting seems idea is great and might be worth investing more time, people, money.
passcombo.com 🏴☠🏴01-Aug-18 02:27 PM
For the reason of not wasting time/human resources /(sending cards to people who will never use it) I would suggest to revert the process a bit: 1. user registration and KMD+BTC address creation (if not registered - lowest priority) 2. user optional payment - just anything between 0 and 10 KMD (for sorting out priority list + having money to pay additional people helping with faster cards creation 3. preparing and sending batches according priority list 4. iterate (edited)
mmm if I look with incognito browser the post is not visible
Gordon Komodo01-Aug-18 03:50 PM
i was about to upvote , but it is removed
KomodoWorld01-Aug-18 04:15 PM
I contacted the mod
it's a new account
passcombo.com 🏴☠🏴01-Aug-18 04:52 PM
@Gordon Komodo thx for sharing. Personally I treat card-coins (created only on purpose of the card) as scams or ponzi. No need to create new coin just for card purpose. You can use existing once -> does not make any sense to create a new one -> shady thing.
The exception could be if the card-coin is revenue based (you buy shares of company) or sth, but I believe mostly purpose is to create new coin, hype it with the purpose of card (like there was no other possibility without?) and dump it later sucking money from "lucky investors"
Actually making revenue coin for perr2group card could be interesting - get money for bigger marketing of the card and promise some % of charged fees in the future. But a decent plan and market knowledge needed before starting.
Gordon Komodo01-Aug-18 05:16 PM
I posted the article just to show that there is huge demand for cards. I think it is because people are so used to it.(edited)
drvasco01-Aug-18 08:01 PM
Vote in KOMODO . If you sign up for this exchange get 20 coins and vote in Komodo to be listed, the top 10 will be part of the list of p trade coins: https://oodltrack.com/tracker.php?affid=5089&l=0
Komodo kann es mit vielen seiner Konkurrenten aufnehmen. Dabei bezeichnet sich Komodo selbst nicht als ein Konkurrent von, sondern sieht sich als Bereicherung für den kompletten Kryptomarkt.
Someone is asking if it's paid or not, which I assume it is bounty-wise? @KomodoWorld
Virginia (Peer2Group)02-Aug-18 08:56 AM
@passcombo.com 🏴☠🏴 actually Peer2krd is just the beginning of what's to come. Stay tuned, we will bring you more exciting news soon
3
KomodoWorld02-Aug-18 09:08 AM
@Cryptoon answered
Deleted User02-Aug-18 09:10 AM
I think someone should make an explain like im 5 for komodos tech
The recent posts (5 pillars) helped a lot of people understand, but theres still the 80% who don't really know much beyond "blockchain is a ledger or something" and don't understand things unless its put into legit baby sentences for them
2
KomodoWorld02-Aug-18 09:13 AM
not a bad idea at all
Deleted User02-Aug-18 09:13 AM
I think go less into detail into exactly how it works (the ones who already know a bit about tech/work for blockchain etc will do their own research and ask questions) but the general public mostly don't care much for that side of things
its hard to see how we could explain things much simpler than theyre already put I think LOL but thats partly due to already understanding or being around komodo for so long, just as a test I showed the articles to some of my friends who are the average casual investor
and they didnt understand as much as I thought they wouldve
though im not sure if targetting that audience/wider public is something that komodo is aiming to achieve at the moment
blackjok3r02-Aug-18 10:19 AM
I was telling my mate about the scaling test... Hes like why is a million transactions important thats just a number, I dont care.
He said specifically we need to say why it benefits him, and why should he care about it. He wasn;t being rude, he just said that sthe only why you will make people care about it.
1 million transactions per second, btw:
1) Its cheaper for you to use the platform than any other
2) Increases your security and anonymity as there are so many TX's its almost impossible to trace a single one.
3) its more reliable for your use and wont fail when too busy
Regnar02-Aug-18 11:31 AM
4) it's not 1M tx/s for Komodo Platform that is shared among all chains built on it. Every chain is capable of that scaling, and even scaling tx/s greater than Komodo itself. (Might just be a point to make)
5) Tx/s don't just mean individual people buying things. They can mean messages sent, things like tweets, products communicating with each other, buying and selling to one another, automated micro transactions. which allows for scaling of any type of product or service imaginable.
Regnar02-Aug-18 11:40 AM
I read something, think it was on XRP tbh, but they talked about autonomous cars/uber communicating with one another and send micro transactions to allow cars to tip one another to move through traffic faster. Or the conversation with Richard Gendal Brown in which the famous quote comes, "on the blockchain, nobody knows you're a fridge". I think when we get out of the mindset that a transaction is a person walking into a store and swiping a card to buy something, and instead we realize the world of microtransactions, of instant, automated transactions by people, and automated transactions by machines. we realize that the even with half a century that VISA has had to build infrastructure clearly isn't effective enough to handle these types of transactions. hell, VISA crashed in Europe two months ago, failing to do exactly what it was designed to do, individual human transactions.
Deleted User02-Aug-18 11:41 AM
I are a fridge
Regnar02-Aug-18 11:41 AM
or we realize that things like email, twitter, text messaging, or any form of communication COULD (not saying they all should) exist on a chain, or form of chain, it's easier to understand that the 1M tx/s milestone, while arbitrary in scale, is important as a benchmark to competitors.
Deleted User02-Aug-18 11:42 AM
Not looking forward to that future hahahhaha id rather spend money on a turbo than to tip other drivers on the blockchain makes me wonder what the worlds gonna look like when im in my 30 40s
Regnar02-Aug-18 11:43 AM
If we said 7.4B transactions a second we could say it's important because every person on earth could transact at once every second. or however many fridges there are, cars, etc. it's easy to see how even the 1M mark could be overshadowed haha
also
On the blockchain nobody knows that you're a doge.
Deleted User02-Aug-18 11:44 AM
But the blockchain knows inb4 church of blockchain
Mark my words some lunatics gonna start a blockchain cult within the next few years
@KomodoWorld @aantonop It'd be nice to know what you think of #CryptoConditions Andreas
https://t.co/rDH9M2C2CZ
Deleted User02-Aug-18 12:01 PM
Mylo02-Aug-18 01:44 PM
nice one satinder
@Regnar yeah humans aren't going to be doing the most transactions. we're moving from a time where people paid for software, to people & machines paying for services - it's the original IBM SaaS vision from 20 years ago - and now these services (and devices that use these services) have to pay themselves to be maintainable by humans and other machines.
the economy now involves "robots". they create value, and now that value is programmable. they're pseudo-autonomous - and nobody wants to pay for software to run these machines. my 2c
Regnar02-Aug-18 01:53 PM
Yea, "transaction" is in people's minds as a human transaction for goods, either on Amazon, in a store, or with someone else, and though IOT and robots buying/selling/transacting has been known about for decades, it seems most people don't see it on the horizon.
Just looking at VISA infrastructure, or traditional banking infrastructure and the use of ACH systems to clear transactions at T+3 its laughable to think that they will be able to handle these micro transactions, or non human transactions (transactions of monetary value, or even information)
The problem I see for us is, internet, coding, and even blockchain is like clay for bricks. it's difficult to show the builder they can build a house from this clay. But if we turn that clay into bricks, move our product/service one step down the line, it's much easier to show them they can build a house of bricks. So we either need to take that step ourselves and develop these bricks for use, or we need to find others who are willing to turn our clay to bricks and then sell it themselves.
Not a perfect example obviously
Regnar02-Aug-18 02:11 PM
So I guess by this analogy it is up to us to identify the markets that each Komodo product or service is designed for, either as of now, or planned in the future.
ie. selling bricks to builder, or selling clay to brick maker
Regnar02-Aug-18 02:21 PM
@Mylo are you typing?
Mylo02-Aug-18 02:21 PM
it's spot on. kmd does a lot of things first. we're a small focussed team. and you're right, we don't know all the potential markets for our tech. for one group, it's empowering - for another a convenience. in one culture it's accepted in another it needs to work a different way. we are all pioneers. the other blockchain groups too.
the server for the "community news site" has been provisioned - probably be a few days til it's testable.
does it say i'm typing the whole time? i'm doing a cpl things at once
Regnar02-Aug-18 02:22 PM
yea haha it did
Mylo02-Aug-18 02:23 PM
ok weird. i'm using franz app which holds discord in webview along with slack, whatsapp etc.
Regnar02-Aug-18 02:26 PM
yea no worries.
It would be worthwhile to get a list of Komodo capabilities, features, or functions, so we can see which are clay and which are bricks. Also which aren't "products" yet but could be, and which ones are products that have limited use cases and would be better bundled with others.
Why $KMD is my biggest bag.
#jointherevolution
I just published “KOMODO, the True King of Crypto.” https://t.co/38CVpzfCr7
7
Steve Lee02-Aug-18 09:50 PM
@Regnar we're working on something like that
2
RAE02-Aug-18 11:59 PM
Sirin Labs are looking for a next-gen blockchain which can handle their daily FINNEY smart-phone payments.
Komodo's ecosystem should be able to handle all those ... for now they're stuck using ETH but the CEO indicates they need to upgrade.
Perhaps Komodo marketing team could try and make contact if they haven't already.
10
5
5
Steve Lee03-Aug-18 12:50 AM
Thanks for the tip, i've reached out to Moshe.
3
Regnar03-Aug-18 01:21 AM
I also mentioned it to their devs, gave them your email @Steve Lee
Gordon Komodo03-Aug-18 01:28 AM
I have to say I am proud of our marketing channel and teams!(edited)
9
1
Steve Lee03-Aug-18 01:57 AM
thanks @Regnar
2
Regnar03-Aug-18 02:05 AM
Figured you went high, I'll go low lol
Steve Lee03-Aug-18 02:09 AM
the good ole sandwich attack strategy
3
5
KomodoWorld03-Aug-18 09:14 AM
I found a technical writer with good skills in case there are still positions for this job


LOCALTOGLOBAL3D create transform from a vector and 3 rotation angles TRANS = localToGlobal3d(CENTER, THETA, PHI, PSI) Example localToGlobal3d See also transforms3d ------ Author: David Legland e-mail<EMAIL_ADDRESS>Created: 2009-06-19, using Matlab <IP_ADDRESS>1 (R2008b) Copyright 2009 INRA - Cepia Software Platform.


If you have any problems or questions, please contact us via e-mail at: {{ site.data.conference.email }}


Learning to Recommend via Inverse Optimal Matching
Ruilin Li, Xiaojing Ye, Haomin Zhou, Hongyuan Zha [ pdf ]
PhD student @ Georgia Tech
I am currently a second year Ph.D. student in Computational Science and Engineering(CSE) @ Georgia Tech. I am fortunate to be advised by Prof. Hongyuan Zha. I obtained my B.S in Mathematics from Fudan University, Shanghai, under supervision of Prof. Wei Lin.
Ruilin Li, Xiaojing Ye, Haomin Zhou, Hongyuan Zha [ pdf ]
Email<EMAIL_ADDRESS>Office: Skiles 146B
Address: 686 Cherry St NW, Atlanta, GA 30313


The components
- Interface
- Versions
- And in the future..
- Legal issues
- FAQ
- Download
TGzip
- TCrc32
- TAdler32
- TUnTar
TCrc32
Properties
| About |
guess what :-) |
| Crc32FileName |
Name of the file from which to compute the Crc32 value. |
| Progress |
Percentage of data that is already processed. |
| ProgressStep |
Value from 0..100. A value of 11 generates an event when 11%, 22%,
33%, ... 99%, 100% of the data is processed. 0 disables this event and
100 only triggers the event when all the data is processed. |
| WindowOnError |
If set, the component will show a message to the user. You can disable
this and inform (or not) the user the way you want. |
Functions
| CalcCrc32 |
Returns the Crc32 value of the file denoted in Crc32FileName. |
| CalcCrc32_hex |
Returns a string representing the hexadecimal Crc32 value of the file
denoted in Crc32FileName. |
Events
| OnProgress |
After completion of ..% (see ProgressStep) this event will be triggered. |
|


Baylor football: Matt Rhule says QB Charlie Brewer is fine
by Berry TramelBaylor quarterback Charlie Brewer suffered a hard hit in the fourth quarter against Texas on Saturday and didn’t return to the game, but Bears coach Matt Rhule said Monday that Brewer is fine and that his injury was ankle-related.
“He’s good,” Brewer said Monday on the Big 12 football teleconference. “He probably could have come back in the game. But it was smart to keep him out. Really good opportunity to play Gary (Bohanan, the backup).”
Brewer appear to be shaken up from the hit in the head area, but Rhule said Brewer suffered a rolled ankle.
“Kind of gimpy on Saturday but saw him this morning, looks good. Looked good last night. I think he’ll be ready to roll.”
Baylor hosts Kansas on Saturday, then plays OU in the Big 12 Championship Game in Arlington on December 7.
Related Photos


| Package | Description |
|---|---|
| flocksimulator.benchmark.mock | |
| flocksimulator.domain | |
| flocksimulator.util |
| Modifier and Type | Method and Description |
|---|---|
Vector |
MockAgent.alignment(FlockList<Agent> agents)
Method for creating average velocity of other agents within view radius
of this agent, and creating a steering force to correct this agents
heading based on that average.
|
void |
MockAgent.applyBehaviors(FlockList<Agent> agents,
Vector target) |
Vector |
MockAgent.cohesion(FlockList<Agent> agents)
Method for creating a force to be attracted to center of flock made of
other agents if they are within the view radius of this agent.
|
void |
MockAgent.flock(FlockList<Agent> agents)
Combine behaviors for flocking, and scale them by externally set
modifiers.
|
Vector |
MockAgent.separation(FlockList<Agent> agents)
Method for creating a force by which to repel from other agents based on
their distance if they are within the view radius of this agent.
|
| Modifier and Type | Field and Description |
|---|---|
protected FlockList<Agent> |
Generator.agents |
| Modifier and Type | Method and Description |
|---|---|
FlockList<Agent> |
Generator.getAgents() |
| Modifier and Type | Method and Description |
|---|---|
Vector |
Flocker.alignment(FlockList<Agent> agents)
Method for creating average velocity of other agents within view radius
of this agent, and creating a steering force to correct this agents
heading based on that average.
|
abstract void |
Agent.applyBehaviors(FlockList<Agent> agents,
Vector target)
Combine behaviors to create more complex behavior
|
void |
Flocker.applyBehaviors(FlockList<Agent> agents,
Vector target) |
Vector |
Flocker.cohesion(FlockList<Agent> agents)
Method for creating a force to be attracted to center of flock made of
other agents if they are within the view radius of this agent.
|
void |
Flocker.flock(FlockList<Agent> agents)
Combine behaviors for flocking, and scale them by externally set
modifiers.
|
Vector |
Flocker.separation(FlockList<Agent> agents)
Method for creating a force by which to repel from other agents based on
their distance if they are within the view radius of this agent.
|
| Modifier and Type | Method and Description |
|---|---|
FlockList<T> |
BinLattice.getNearestNeighbors(int posX,
int posY)
Query neighbors for specified position.
|
Copyright © 2018. All rights reserved.


Tree¶
A TreeWidget object can contain TreeItems, which in turn, can contain TreeItems to construct a tree. First an example flat list with items that are selectable and checkable:
from flexx import app, ui
class Example(ui.Widget):
def init(self):
with ui.TreeWidget(max_selected=2):
for t in ['foo', 'bar', 'spam', 'eggs']:
ui.TreeItem(text=t, checked=False)
Next, a tree example illustrating connecting to various item events, and custom styling:
from flexx import app, event, ui
class Example(ui.Widget):
CSS = '''
.flx-TreeWidget {
background: #000;
color: #afa;
}
'''
def init(self):
with ui.BoxPanel():
self.label = ui.Label(flex=1, style='overflow-y: scroll;')
with ui.TreeWidget(flex=1, max_selected=1) as self.tree:
for t in ['foo', 'bar', 'spam', 'eggs']:
with ui.TreeItem(text=t, checked=None):
for i in range(4):
item2 = ui.TreeItem(text=t + ' %i'%i, checked=False)
if i == 2:
with item2:
ui.TreeItem(title='A', text='more info on A')
ui.TreeItem(title='B', text='more info on B')
class JS:
@event.connect('tree.items**.checked', 'tree.items**.selected',
'tree.items**.collapsed')
def on_event(self, *events):
for ev in events:
id = ev.source.title or ev.source.text
if ev.new_value:
text = id + ' was ' + ev.type
else:
text = id + ' was ' + 'un-' + ev.type
self.label.text = text + '<br />' + self.label.text
-
class
flexx.ui.
TreeItem
(*args, **kwargs)¶ Inherits from:
Model
An item to put in a TreeWidget. TreeItem objects are Model objects, but do not inherit from ui.Widget.
Items are collapsable/expandable if their
collapsed
property is set toTrue
orFalse
(i.e. notNone
), or if they have sub items. Items are checkable if theirchecked
property is set toTrue
orFalse
(i.e. notNone
). Items are selectable depending on the selection policy defined byTreeWidget.max_selected
.-
checked
¶ property – Whether this item is checked (i.e. has its checkbox set). The value can be None, True or False. None (the default). means that the item is not checkable.
-
collapsed
¶ property – Whether this item is expanded (i.e. shows its children). The value can be None, True or False. None (the default) means that the item is not collapsable (unless it has sub items).
-
items
¶ property – The list of sub items.
-
mouse_click
¶ emitter – Event emitted when the item is clicked on. Depending on the tree’s max_selected, this can result in the item being selected/deselected.
-
mouse_double_click
¶ emitter – Event emitted when the item is double-clicked.
-
selected
¶ property – Whether this item is selected. Depending on the TreeWidget’s policy (max_selected), this can be set/unset on clicking the item.
-
text
¶ property – The text for this item. Can be used in combination with
title
to obtain two columns.
-
title
¶ property – The title for this item that appears before the text. Intended for display of key-value pairs. If a title is given, the text is positioned in a second (virtual) column of the tree widget.
-
visible
¶ property – Whether this item (and its sub items) is visible.
-
-
class
flexx.ui.
TreeWidget
(*init_args, **kwargs)¶ Inherits from:
Widget
A Widget that can be used to structure information in a list or a tree. To add items, create TreeItem objects in the context of a TreeWidget. Sub items can be created by instantiating TreeItems in the context of another TreeItem.
When the items in the tree have no sub items, the TreeWidget is in “list mode”. Otherwise, items can be collapsed/expanded etc. This widget can be fully styled using CSS, see below.
Style
Style classes applied to the TreeWidget:
flx-listmode
is set on the widget’s node if no items have sub items.
Style classes for a TreeItem’s elements:
flx-TreeItem
indicates the row of an item (its text, icon, and checkbox).flx-TreeItem > collapsebut
the element used to collapse/expand an item.flx-TreeItem > checkbut
the element used to check/uncheck an item.flx-TreeItem > text
the element that contains the text of the item.flx-TreeItem > title
the element that contains the title of the item.
Style classes applied to the TreeItem, corresponding to its properties:
visible-true
andvisible-false
indicate visibility.selected-true
andselected-false
indicate selection state.checked-true
,checked-false
andchecked-null
indicate checked state, with thenull
variant indicating not-checkable.collapsed-true
,collapsed-false
andcollapsed-null
indicate collapse state, with thenull
variant indicating not-collapsable.
-
get_all_items
()¶ Get a flat list of all TreeItem instances in this Tree (including sub items and sub-sub items, etc.).
-
items
¶ property – The list of (direct) TreeItem instances for this tree.
-
max_selected
¶ property – The maximum number of selected items. Default 0. Can be -1 to allow any number of selected items. This determines the selection policy.


Home
Advantages
Breakfasts
Contact us
Dashboard
Retry
Error 500
, there is an error with the server.
NextLevel team
apologize
NextLevel
Login to access the Dashboard
Email
Password
Submit


17 August 2019
SUBJECT: EXAMPLES OF REDACTED.CSS USED IN THE FIELD
There evidence to support that Redacted.css will work on all modern browsers.
Nevertheless if an issue were to arise, it is recommended that an issue report is filed on the project page in order to quickly eliminate these issues.
Brennan Vargas


Too often our health must take the back seat for what really is important, it is only when you lose your health that you realise how important it is. We are in the business of getting people's health back and teaching them how to stay healthy.
Bio Health houses 4 Biokineticists with over 20 years combined experience. Biokinetics is comprised of orthopaedic rehabilitation (strengthening of muscles to protect joints during both sport and activities of daily living), chronic disease management (prescription and monitoring of individualised cardiovascular exercise) and total wellness (lifestyle and stress management). Recognised by and registered with the HPCSA (Health Professions council of South Africa), Biokinetics is the field of specialised exercise therapy which functions in alliance to both the health and medical industries .


|
Student ID No.
<#user.Identifier#>; <#Username#> |
Dear <#Name#>,
We're excited that you'll be joining a Masters programme with us on <#user.ProgrammeStartDate#>.
You will already have received your log-in details to Laureate LENS, where you access the online classroom, the student community, and the student finance portal. In this email we want to provide you with a summary of the activities you need to complete in the run-up to the start of your programme.
Getting Ready to StudyAs you wait for your programme to begin, we'd like to ask you to start the New Student Preparation Programme; this is designed to help you prepare for life as an online postgraduate student. This consists of:
Please read more about LEAP before you log-in for the first time, so you can get the full benefit of the assessment. Your login details are below:
Email Address: <#user.LEAPEmail#>; <#Email#>>
Password: <#user.LEAPPassword#>; <#Password#>
A sequence of webinars that will help you prepare to study.
You will be sent details of each live event separately.
This is a compulsory programme you must complete before you can start your studies; you can begin your OIC on <#user.OICStart#> and must complete it by <#user.OICEnd#> to start your programme as scheduled.
We know that students who undertake the New Student Preparation Programme are more successful in their first module, so engaging fully in the above activities will help ensure you get the best possible result from the investment you are making in your future.
If you have any questions about the New Student Preparation Programme or completing the Online Induction Course or LEAP, don't hesitate to reach out to your Enrolment Advisor.
Yours faithfully,
Student Administration Centre
Please do not reply to this email, the address is only used for sending messages and you will not receive a response. If you have any questions or need to contact us please click here.
| University of Liverpool Online Programmes are delivered in partnership with: |


--- layout: default title: Home ---
I completed my PhD in Mathematics at the University of Iowa. My research focuses on spatial, spatiotemporal, and temporal statistics, with a special focus on Bayesian viewpoints.


About me
My Name Is MBP . I just recently graduated secondary school and im currently a web developer in training soon to be a master of the craft. I never saw this,to be an area which id have dived into back in my secondary school days but beginning this journey opened my eyes to new things and has helped widen my horizon.Learning coding in general is a fun and tiresome experience,it has a bitter sweet feel to it(no sugarcoating),it really is a wonderful experience to learn the roots of web designing and development, and i hope to improve every step of the way.


We got platforms!
- transition collaborative action-items
- brand world-class schemas
- evolve killer interfaces
- streamline proactive experiences
- evolve revolutionary convergence
- transform enterprise supply-chains
- engage end-to-end deliverables
- generate dot-com web-readiness
- maximize 24/365 portals
- drive frictionless ROI
- deploy holistic infomediaries
- reintermediate one-to-one paradigms


Giving Is Not Just About Make a Donation,its about making difference
BE THE CHANGE YOU WANT TO SEE IN THIS WORLD


Snap-Chatting Cheerleader Defends Speech Rights at 3rd Circuit
by EMILEE LARKINPHILADELPHIA (CN) — A lawyer for a cheerleader kicked off of her high school squad over a scurrilous Snap post urged the Third Circuit on Tuesday to affirm their courtroom victory.
“Schools can’t reach outside of their authority and punish students for outside speech,” American Civil Liberties attorney Sara Rose argued before the federal appeals court in Philadelphia. “That is dangerous.”
The case began when the teen, referred to only by her initials B.L., did not make the varsity cheer squad at Mahanoy Area School District for the 2017-18 school year.
Frustrated that she was assigned to the JV team for another year, B.L. took to the app formerly known as Snapchat while at a local convince store with a friend.
The post made its way back to her coaches who decided she should be kicked off the team all together. Her parents, Lawrence and Betty Lou Levy, filed suit, persuading a federal judge this year at summary judgment that their daughter’s words are protected under the First Amendment.
Michael Levin, a lawyer based in Huntingdon Valley, is leading the school’s appeal. He argued to the three-judge panel Tuesday morning that the social media post was about the school, so it had every right to remove her from extracurricular activities.
“This post was definitely related to school context,” Levin said. “It was sent to other students and referred directly to school.”
U.S. Circuit Judge Cheryl Ann Krause understood his point, but expressed concern on how it would translate to other students.
“How do we draw that line,” asked Krause, an Obama appointee. “Being kicked off extracurricular activities for speech would chill other students.”
Levin agreed but stressed the importance for the school to have room to discipline.
The Levys’ lawyer pushed back, urging the panel that ruled aimed at disciplining students for outside speech can get dicey.
“There has to be some breathing room for students to engage in speech,” said Rose. “Putting these rules in place makes it very hard to draw the line.”
Krause noted that student-athletes are often looked at as a representative of the school, and questioned why they should not be punished for things said about the school even if they are not on campus.
Rose disagreed, arguing that it is not fair to place that role on kids.
“Students are the customers of the school, not representatives,” said Rose.
U.S. Circuit Judge Stephanos Bibas raised a hypothetical point about when a school should step in.
“What about the student who brags about his gun collections and talks about Columbine and Sandy Hook, do you think the school is unable to punish him even though it’s not a threat,” asked Bibas, a Trump appointee.
Rose agreed that is a serious concern that should face consequences.
Frank LeMonte, director of the Brechner Center for Freedom of Information at the University of Florida, has been following this case closely and says it’s an important one for students rights.
“Schools frequently argue that they can take away ‘privileges’ freely and without any constitutional redress, but that terribly misconceives the way that the First Amendment works, and the Third Circuit should unequivocally say so,” LeMonte said in an email.
LeMonte further stressed that it is not the schools’ place to handle off campus speech.
“If a school has the authority to police off-campus speech with the same level of control as on-campus speech, then schools are going to be fully in the parenting business, refereeing minor name-calling disagreements that ought to be settled by the students and their families,” LeMonte continued.
LeMonte pointed out that it would be “inconceivable” if we allowed any other government agency to punish people for expressing their dissatisfaction.

A famous precedent invoked by both parties at oral arguments comes from the 1969 Supreme Court ruling in Tinker v. Des Moines Independent School District, a case where students were suspended for wearing black armbands in protest to the Vietnam War, that for schools to censor speech it must hat student expression would lead to either a substantial disruption of the school environment, or an invasion of other students’ rights.
Attorneys for both parties did not immediately respond to emails seeking comment.
The panel was rounded out by U.S. Circuit Judge Thomas Ambro.
Copyright © 2019 courthousenews.com


This movie is amazingly quotable. This list does not do it justice, but here are a couple of my favs.
Quotes from Madeline:
- "Wrinkled, wrinkled little star... hope they never see the scars."
- "NOW a warning?"
- "Oh, for Christ's sake, at least lie quickly!"
Quotes from Helen:
- "She married a brilliant surgeon, and turned him into an undertaker."
- "She was a homebreaker. She was a man-eater. And she was a *bad* actress."
- "She's from Newark for Christ's sake."


We operate in an industry built on trust. This can only be achieved through
communication and experienced support – from the first contact past your
ten-year anniversary.
You're not going to hit a ridiculously long phone menu when you call us.
Your email isn't going to the inbox abyss, never to be seen or heard from again.
At Choice Screening, we provide the exceptional service we'd want to experience
ourselves!


Getting Started
Seneca lets you build a microservice system without worrying about how things will fit together in production. You don't need to know where the other services are located, how many of them there are, or what they do. Everything external to your business logic, such as databases, caches, or third party integrations can likewise be hidden behind microservices.
This decoupling makes your system easy to build and change on a continuous basis. It works because Seneca has two core features.
- Transport Independence: you can send messages between services in many ways, all hidden from your business logic.
- Pattern Matching: instead of fragile service discovery, you just let the world know what sort of messages you care about.
Messages are JSON documents, with any internal structure you like. Messages can be sent via HTTP/S, TCP connections, message queues, publish/subscribe services or any mechanism that moves bits around. From your perspective as the writer of a service, you just send messages out into the world. You don't want to know which services get them — that creates fragile coupling.
Then there are the messages you'd like to receive. You specify the property patterns that you care about, and Seneca (with a little configuration help) makes sure that you get any messages matching those patterns, sent by other services. The patterns are very simple, just a list of key-value pairs that the top level properties of the JSON message document must match.
This Getting Started guide will cover Seneca in a broad way, but won't go into too much depth. The Next Steps section at the end of this Page is your starting point for more details.
A Simple Microservice
Let's start with some code. Here's a service that sums two numbers:
var seneca = require( 'seneca' )()
seneca.add(
{role:'math', cmd:'sum'},
function( msg, respond ) {
var sum = msg.left + msg.right
respond( null, { answer: sum } )
})
To call this service, you write:
seneca.act(
{role:'math', cmd:'sum', left:1, right:2},
function( err, result ) {
if( err ) return console.error( err )
console.log( result )
})
For the moment this is all happening in the same process, and there's no network traffic. In-process function calls are a type of message transport too!
The example code to try this out is in sum.js. To run the code, follow these steps:
- Open a terminal, and
cd
to your projects folder. - Run
git clone https://github.com/senecajs/getting-started
. cd
into the getting-started folder.- Run
npm install
to install the required modules, including Seneca. - Run
node sum.js
.
This guide assumes you already have Node.js installed.
When you run sum.js
, you get the following output:
2015-07-02T12:38:08.788Z xi94dnm0nrky/1435840688779/64029/- INFO hello Seneca/0.6.2/xi94dnm0nrky/1435840688779/64029/-
{ answer: 3 }
The first line is logging information that Seneca prints to let you know that it has started. The second line is the result produced after the message has been matched and processed.
The seneca.add
method adds a new action pattern to the Seneca
instance. This pattern is matched against any JSON messages that the
Seneca instance receives. The action is a function that is executed
when a pattern matches a message.
The seneca.add
method has two parameters:
pattern
: the property pattern to match in messages,action
: the function to execute if a message matches.
The action function has two parameters:
msg
: the matching inbound message (provided as a plain object),respond
: a callback function that you use to provide a respond to the message.
The respond function is a callback with the standard error, result
signature. Let's put this all together again:
← pattern to match
← action function
← the real work, adding two numbers
← provide a result object; there's no error
seneca.add(
{role:'math', cmd:'sum'},
function( msg, respond ) {
var sum = msg.left + msg.right
respond( null, { answer: sum } )
})
In the example code, the action computes the sum of two numbers,
provided via the left
and right
properties of the message
object. Not all messages generate a result, but as this is the most
common case, Seneca allows you to provide the result via a callback
function.
In summary, the action pattern role:math,cmd:sum
acts on the message
{ "role": "math", "cmd": "sum", left:1, right:2 }
to produce the result:
{ "answer": 3 }
There is nothing special about the properties role
and cmd
. They
just happen to be the ones you are using for pattern matching.
The seneca.act
method submits a message to act on. It takes two parameters:
msg
: the message object,response_callback
: a function that receives the message response, if any.
The response callback is a function you provide with the standard
error, result
signature. If there was a problem (say, the message
matched no patterns), then the first argument will be an
Error
object. If everything went to plan, the second argument will be the
result object. In the example code, these arguments are simply printed
to the console:
seneca.act(
{role:'math', cmd:'sum', left:1, right:2},
function( err, result ) {
if( err ) return console.error( err )
console.log( result )
})
The example code in the sum.js file shows you how to define and call an action pattern inside the same Node.js process. Soon you'll see how to split this code over multiple processes.
How Patterns Work
Using patterns instead of network addresses or topics makes it much easier to extend and enhance your system over time by adding new microservices incrementally. Let's extend our system with the ability to multiply two numbers.
We want messages that look like this:
{ "role": "math", "cmd": "product", left:3, right:4 }
to produce results like this:
{ "answer": 12 }
You can use the role:math,cmd:sum
action pattern as a template to
define a new role:math,cmd:product
action:
seneca.add(
{role:'math', cmd:'product'},
function( msg, respond ) {
var product = msg.left * msg.right
respond( null, { answer: product } )
})
And you can call it in exactly the same way:
seneca.act(
{role:'math', cmd:'product', left:3, right:4},
console.log
)
Here, you use console.log
as a shortcut to print out both the error
(if any), and the result. Running this code produces:
null { answer: 12 }
Putting this all together, you get:
var seneca = require( 'seneca' )()
seneca.add(
{role:'math', cmd:'sum'},
function( msg, respond ) {
var sum = msg.left + msg.right
respond( null, { answer: sum } )
})
seneca.add(
{role:'math', cmd:'product'},
function( msg, respond ) {
var product = msg.left * msg.right
respond( null, { answer: product } )
})
seneca
.act(
{role:'math', cmd:'sum', left:1, right:2},
console.log
)
.act(
{role:'math', cmd:'product', left:3, right:4},
console.log
)
In this example the seneca.act
calls are chained together. Seneca
provides a chaining API as a convenience. Chained calls are executed
in order, but not in series, so their results could come back in any
order.
This code is available in the sum-product.js file.
Extending Functionality with Patterns
Patterns make it easy to extend your functionality. Instead of adding
if
statements and complex logic, you add more patterns. Lets extend
the addition action with the ability to force integer-only
arithmetic. To do this, you can add a new property, integer:true
to
the message object. Then you provide a new action for messages that
have this property:
seneca.add(
{role:'math', cmd:'sum', integer:true},
function( msg, respond ) {
var sum = Math.floor(msg.left) + Math.floor(msg.right)
respond( null, { answer: sum } )
})
Now, the message:
{ "role": "math", "cmd": "sum", left:1.5, right:2.5, integer:true }
will produce the result:
{ "answer": 3 } // == 1 + 2, as decimals removed
What happens if you add both patterns to the same system? How does Seneca choose which one to use? The more specific pattern always wins. That is, the pattern with the most matching attributes is the one that has precedence.
Here's some code to show this:
var seneca = require( 'seneca' )()
seneca
.add(
{role:'math', cmd:'sum'},
function( msg, respond ) {
var sum = msg.left + msg.right
respond( null, { answer: sum } )
})
// both these messages will match role:math,cmd:sum
.act(
{role:'math', cmd:'sum', left:1.5, right:2.5},
console.log // prints { answer: 4 }
)
.act(
// the extra integer property is just ignored!
{role:'math', cmd:'sum', left:1.5, right:2.5, integer:true},
console.log // prints { answer: 4 }
)
.add(
{role:'math', cmd:'sum', integer:true},
function( msg, respond ) {
var sum = Math.floor(msg.left) + Math.floor(msg.right)
respond( null, { answer: sum } )
})
// this still matches role:math,cmd:sum
.act(
{role:'math', cmd:'sum', left:1.5, right:2.5},
console.log // prints { answer: 4 }
)
// BUT this matches role:math,cmd:sum,integer:true
// because it's more specific - more properties match
.act(
{role:'math', cmd:'sum', left:1.5, right:2.5, integer:true},
console.log // prints { answer: 3 } !!!
)
And the output it generates is:
2015-07-02T15:34:06.094Z 69kfu5lr238x/1435851246084/64488/- INFO hello Seneca/0.6.2/69kfu5lr238x/1435851246084/64488/-
null { answer: 4 }
null { answer: 4 }
null { answer: 4 }
null { answer: 3 }
The first two .act
calls both match the role:math,cmd:sum
action
pattern. Then the integer-only action pattern
role:math,cmd:sum,integer:true
is defined. After that, the third
call to .act
gos with the role:math,cmd:sum
action, but the fourth
goes with role:math,cmd:sum,integer:true
. This code also
demonstrates that you can chain .add
and .act
calls together. This
code is available in the
sum-integer.js
file.
The ability to easily extend the behaviour of your actions by matching more specific kinds of messages is an easy way to handle new and changing requirements while your project is in development and when it is live and needs to adapt. It also has the advantage that you do not need to modify existing code, which is always dangerous. It's much safer to add new code to handle special cases. In a production system you won't even need to do a re-deploy. Your existing services can stay running as they are. All you need to do is start up your new service.
Code Re-use with Patterns
Action patterns can call other action patterns to get their work done. Let's modify our example code to use this approach.
var seneca = require( 'seneca' )()
seneca
.add(
'role:math,cmd:sum',
function( msg, respond ) {
var sum = msg.left + msg.right
respond( null, { answer: sum } )
})
.add(
'role:math,cmd:sum,integer:true',
function( msg, respond ) {
// reuse role:math,cmd:sum
this.act({
role: 'math',
cmd: 'sum',
left: Math.floor(msg.left),
right: Math.floor(msg.right),
}, respond)
})
// this matches role:math,cmd:sum
.act( 'role:math,cmd:sum,left:1.5,right:2.5',
console.log // prints { answer: 4 }
)
// BUT this matches role:math,cmd:sum,integer:true
.act( 'role:math,cmd:sum,left:1.5,right:2.5,integer:true',
console.log // prints { answer: 3 } !!!
)
In this version, the definition of the
role:math,cmd:sum,integer:true
action pattern uses the previously
defined role:math,cmd:sum
action pattern, but first modifies the
message to convert the left
and right
properties into integers.
Inside the action function, the context variable, this
, is a
reference to the current Seneca instance. This is the proper way to
reference Seneca inside actions, as you will get the full context of
the current action call. Among other things, this makes your logs more
informative.
This code uses an abbreviated form of JSON to specify the patterns and messages. For example, the object literal form
{role:'math', cmd:'sum', left:1.5, right:2.5}
becomes:
role:math,cmd:sum,left:1.5,right:2.5
This format, jsonic, which you provide as a string literal, is a convenience format to make patterns and messages more concise in your code.
The code for the above example is available in the sum-reuse.js file.
Patterns are Unique, with Overrides
The action patterns that you define are unique. They can only trigger one function. The patterns resolve using the rules:
- more properties win, and
- if the patterns have the same number of properties, they are matched in alphabetical order.
The reason that these rules are so simple is so that you can run them "in your head". It's very easy to understand which pattern will trigger which action function.
Here are some examples:
a:1,b:2
wins overa:1
as it has more properties.a:1,b:2
wins overa:1,c:3
asb
is beforec
alphabetically.a:1,b:2,d:4
wins overa:1,c:3,d:4
asb
is beforec
alphabetically.a:1,b:2,c:3
wins overa:1,b:2
as it has more properties.a:1,b:2,c:3
wins overa:1,c:3
as it has more properties.
To see this in action, run the file pattern-wins.js. For more details, see the patrun module.
It is sometimes useful to have a way of enhancing the behaviour of an action without rewriting it fully. For example, you might want to perform custom validation of the message properties, or capture message statistics, or add additional information to action results, or throttle message flow rates.
In the example code, the addition action expects that the left
and
right
properties are finite numbers. Also, it's useful to include
the original input arguments in the output for debugging purposes. You
can add a validation check, and debugging information, using the
following code:
var seneca = require( 'seneca' )()
seneca
.add(
'role:math,cmd:sum',
function( msg, respond ) {
var sum = msg.left + msg.right
respond( null, { answer: sum } )
})
// override role:math,cmd:sum with additional functionality
.add(
'role:math,cmd:sum',
function( msg, respond ) {
// bail out early if there's a problem
if( !Number.isFinite( msg.left ) ||
!Number.isFinite( msg.right ) )
{
return respond( new Error("Expected left and right to be numbers.") )
}
// call previous action function for role:math,cmd:sum
this.prior({
role: 'math',
cmd: 'sum',
left: msg.left,
right: msg.right,
}, function( err, result ) {
if( err ) return respond( err )
result.info = msg.left+'+'+msg.right
respond( null, result )
})
})
// enhanced role:math,cmd:sum
.act( 'role:math,cmd:sum,left:1.5,right:2.5',
console.log // prints { answer: 4, info: '1.5+2.5' }
)
The Seneca instance provided to an action function via the the this
context variable has a special prior
method that calls the previous
action definition for the current action pattern.
The prior function has parameters:
msg
: the msg object, which you may have modifiedresponse_callback
: a callback function, where you may modify the result
The example code shows you how to modify both the inbound message, and the outbound result. Modification of either is optional — you may leave the data unchanged and use this mechanism for enhanced logging or auditing.
The example code also shows good practices for error handling. It uses
early returns to exit from the action function as soon as
possible. This avoids spurious indentation from if-else
statements. The error is provided using an Error
object. This
ensures stack trace capture and proper handling.
Errors should only be used for invalid input or internal failures. For example, if you are executing a database query that returns no data, that is not an error, just a fact about the database. If the database connection fails, that is an error.
The code for this example is in the sum-valid.js file.
Organising Patterns into Plugins
A Seneca instance is ultimately just a set of action patterns. You can
organize them by using namespacing conventions in your patterns, such as
role:math
. To help with logging and debugging, Seneca supports a
minimalist notion of a plugin to help organize patterns.
A Seneca plugin is just a set of action patterns. The plugin can have a name, which is used to annotate logging entries. Plugins can be given a set of options to control their behaviour. Plugins also provide a mechanism for executing initialisation functions in the correct order. For example, you want your database connection to be established before you try to read data from the database.
A Seneca plugin is a function that has a single parameter
options
. You pass this plugin definition function to the
seneca.use
method. Here is the minimal Seneca plugin (it does
nothing!).
function minimal_plugin( options ) {
console.log(options)
}
require( 'seneca' )()
.use( minimal_plugin, {foo:'bar'} )
The seneca.use
method takes two parameters:
plugin
: plugin definition function, or plugin name,options
: options object for the plugin.
If you run the example code (in file minimal-plugin.js), you'll get the following output:
$ node minimal-plugin.js
2015-07-03T15:23:00.038Z gh7s1570egff/1435936980028/65900/- INFO hello Seneca/0.6.2/gh7s1570egff/1435936980028/65900/-
{ foo: 'bar' }
Seneca provides detailed logging information when it starts, and also
when running. Normally, the log level is set to INFO
which means you
don't see very much. To see all the logs, try:
$ node minimal-plugin.js --seneca.log.all
... lots of log lines ...
You can narrow this down by grepping the log output for log lines relevant to plugin definition.
$ node minimal-plugin.js --seneca.log.all | grep plugin | grep DEFINE
2015... 3qf7... DEBUG plugin basic DEFINE {}
2015... 3qf7... DEBUG plugin transport DEFINE {}
2015... 3qf7... DEBUG plugin web DEFINE {}
2015... 3qf7... DEBUG plugin mem-store DEFINE {}
2015... 3qf7... DEBUG plugin minimal_plugin DEFINE {foo=bar}
You can see that by default Seneca loads four built-in plugins:
basic,
transport,
web, and
mem-store. These
provide core functionalities for basic microservices. You can also see
that your minimal_plugin is in the list as well, and also shown are
the options you provided: {foo=bar}
. The name minimal_plugin is
obtained from the plugin definition function name, so you should
always give your plugin definition function a name.
Let's give the plugin some action patterns. The this
context
variable of the plugin definition function is an instance of Seneca
that you can use to do this. Here's a math plugin:
function math( options ) {
this.add( 'role:math,cmd:sum', function( msg, respond ) {
respond( null, { answer: msg.left + msg.right } )
})
this.add( 'role:math,cmd:product', function( msg, respond ) {
respond( null, { answer: msg.left * msg.right } )
})
}
require( 'seneca' )()
.use( math )
.act( 'role:math,cmd:sum,left:1,right:2', console.log )
Running this file math-plugin.js generates the output:
$ node math-plugin.js
2015-07-03T15:43:13.067Z xn5vquvubvjq/1435938193058/65962/- INFO hello Seneca/0.6.2/xn5vquvubvjq/1435938193058/65962/-
null { answer: 3 }
Let's look at the logging output relevant to this plugin by grepping for the string "math":
$ node math-plugin.js --seneca.log.all | grep math
2015... alqs... DEBUG delegate {plugin$={name=math,tag=undefined},ungate$=true,fatal$=true} 29ny56
2015... alqs... DEBUG register init math
2015... alqs... DEBUG plugin math DEFINE {}
2015... alqs... DEBUG plugin math ADD qlh13h47d0nu cmd:sum,role:math sum
2015... alqs... DEBUG plugin math ADD 10lk4seu3aee cmd:product,role:math product
2015... alqs... DEBUG plugin math options set {math={}}
2015... alqs... DEBUG act - - DEFAULT {init=math,tag=}
2015... alqs... DEBUG register ready math {}
2015... alqs... DEBUG register install math {exports=[]}
2015... alqs... DEBUG act math - IN pg7er4ouia1p/u5hfgtpmkeoy cmd:sum,role:math {role=math,cmd=sum,left=1,right=2} ENTRY A;qlh13h47d0nu -
2015... alqs... DEBUG act math - OUT pg7er4ouia1p/u5hfgtpmkeoy cmd:sum,role:math {answer=3} EXIT A;qlh13h47d0nu 5
There's detailed logging information on the plugin definition and
initialization, which you can mostly ignore for now. The most
interesting lines the ones showing the addition of action patterns
within the math plugin, and then the execution of the
role:math,cmd:sum,left:1,right:2
action, showing the inbound and
outbound messages.
...
20.. al.. DEBUG plugin math ADD ql.. cmd:sum,role:math sum
20.. al.. DEBUG plugin math ADD 10.. cmd:product,role:math product
...
20.. al.. DEBUG act math - IN pg.. cmd:sum,role:math {role=math,cmd=sum,left=1,right=2} ENTRY A;ql.. -
20.. al.. DEBUG act math - OUT pg.. cmd:sum,role:math {answer=3} EXIT A;ql.. 5
...
With Seneca, you build up your system by defining a set of patterns that correspond to messages. You organize these patterns into plugins to make logging and debugging easier. You then combine one or more plugins into microservices. You'll create a math microservice in the next section.
Plugins often need to do some initialization work — such as
connecting to a database. You don't do this work in the body of the
plugin definition function. The definition function is synchronous by
design, because all it does is define the plugin. In fact, you
should not call seneca.act
at all in the plugin definition, just
seneca.add
.
To initailize a plugin, you add a special action pattern:
init:<plugin-name>
. This action pattern is called in
sequence for each plugin serially. The init function must call its
respond
callback without errors. If plugin initialization fails,
then Seneca will exit the Node.js process. You want
your microservics to fail-fast (and scream loudly) when there's a
problem. All plugins must complete initialization before any actions
are executed.
To demonstrate initialization, let's add simplistic custom logging to the math plugin. When the plugin starts, it opens a log file, and writes a log of all operations to the file. The file needs to open successfully and be writable. If this fails, the microservice should fail.
var fs = require('fs')
function math( options ) {
// the logging function, built by init
var log
// place all the patterns together
// this make it easier to see them at a glance
this.add( 'role:math,cmd:sum', sum )
this.add( 'role:math,cmd:product', product )
// this is the special initialization pattern
this.add( 'init:math', init )
function init( msg, respond ) {
// log to a custom file
fs.open( options.logfile, 'a', function( err, fd ) {
// cannot open for writing, so fail
// this error is fatal to Seneca
if( err ) return respond( err )
log = make_log(fd)
respond()
})
}
function sum( msg, respond ) {
var out = { answer: msg.left + msg.right }
log( 'sum '+msg.left+'+'+msg.right+'='+out.answer+'\n' )
respond( null, out )
}
function product( msg, respond ) {
var out = { answer: msg.left * msg.right }
log( 'product '+msg.left+'*'+msg.right+'='+out.answer+'\n' )
respond( null, out )
}
function make_log( fd ) {
return function( entry ) {
fs.write( fd, new Date().toISOString()+' '+entry, null, 'utf8', function(err) {
if( err ) return console.log( err )
// ensure log entry is flushed
fs.fsync( fd, function(err) {
if( err ) return console.log( err )
})
})
}
}
}
require( 'seneca' )()
.use( math, {logfile:'./math.log'} )
.act( 'role:math,cmd:sum,left:1,right:2', console.log )
In this plugin code, the patterns are organized at the top of the plugin so that they are easy to see. The action functions are defined below these. You can also see how the options are used to provide the location for the custom log file (it should go without saying that this is not a way to do production logging!).
The initialization function init does some asynchronous file
system work, and so must complete before any actions can be
performed. If it fails, the whole service will fail to initialize. To
see this in action, try changing the log file location to something
invalid, like say '/math.log'
.
This code is available in the math-plugin-init.js file.
Writing Microservices
Let's turn the math plugin into a real microservice. First, you need to get organized. The business logic of the math plugin, the functionality that it provides, is separate from whatever way it communicates with the outside world. Sometimes you might expose a web service. Other times you might listen on a message bus.
It makes sense to put the business logic, the plugin definition, in its own file. Node.js modules are perfect for this:
module.exports = function math( options ) {
this.add( 'role:math,cmd:sum', function sum( msg, respond ) {
respond( null, { answer: msg.left + msg.right } )
})
this.add( 'role:math,cmd:product', function product( msg, respond ) {
respond( null, { answer: msg.left * msg.right } )
})
this.wrap( 'role:math', function( msg, respond ) {
msg.left = Number(msg.left).valueOf()
msg.right = Number(msg.right).valueOf()
this.prior( msg, respond )
})
}
This plugin is defined in the
math.js
file. You export the plugin definition function, and then call
seneca.use
with the name of the file. You can either
require it in, or if you like
to be terse, let Seneca make the require
call:
// these are equivalent
require( 'seneca' )()
.use( require('./math.js') )
.act( 'role:math,cmd:sum,left:1,right:2', console.log )
require( 'seneca' )()
.use( 'math' ) // finds ./math.js in local folder
.act( 'role:math,cmd:sum,left:1,right:2', console.log )
The seneca.wrap
method matches a set of patterns and overrides all
of them with the same action extension function. This is the same as
calling seneca.add
manually for each one. It takes two parameters:
pin
: a pin is a pattern-matching patternaction
: action extension function
A pin is a pattern that matches other patterns (it "pins" them). The
pin role:math
will match the patterns role:math,cmd:sum
and
role:math,cmd:product
that are registered with Seneca.
In this case, you use seneca.wrap
to make sure that the left
and
right
properties are parsed as numberic values, even if they are
provided as strings.
Sometimes it can be useful to see a visual tree of the patterns and
any overrides in a Seneca instance. You can do this using the
--seneca.print.tree
command line option. The file
math-tree.js loads the math plugin, but then does nothing:
require('seneca')()
.use('math')
We're just using it to show the action tree:
$ node math-tree.js --seneca.print.tree
2015-07-09T15:21:31.158Z 9vjqzroin2k4/1436455291148/78025/- INFO hello Seneca/0.6.2/9vjqzroin2k4/1436455291148/78025/-
Seneca action patterns for instance: 9vjqzroin2k4/1436455291148/78025/-
├─┬ cmd:sum
│ └─┬ role:math
│ └── # math, (s1a28),
│ # math, (sw9ew), sum
└─┬ cmd:product
└─┬ role:math
└── # math, (sxti2),
# math, (b8gcw), product
Here you can see the name/value pairs of the action patterns arranged
in a tree structure, and also any overrides. Action functions are
indicated by the format: # plugin, (action-id), function-name
.
Everything is still in the same process. Let's change that. First you need a microservice:
require( 'seneca' )()
.use( 'math' )
.listen()
Running this code (math-service.js) starts a microservice process that listens on port 10101 for HTTP requests. This is not a web server. In this case, HTTP is being used as the transport mechanism for messages.
You can try it out by sending a request to the microservice. Open the
URL: http://localhost:10101/act?role=math&cmd=sum&left=1&right=2
in
a web browser, or use curl on the command line:
$ curl -d '{"role":"math","cmd":"sum","left":1,"right":2}' http://localhost:10101/act
And what you get back is:
{"answer":3}
Next, you need a microservice client:
require( 'seneca' )()
.client()
.act('role:math,cmd:sum,left:1,right:2',console.log)
Running this code (math-client.js) starts a microservice client that sends the JSON message:
{ "role":"math", "cmd":"sum", "left":1, "right":2 }
to the math-service microservice above, which then responds with:
{ "answer":2 }
With Seneca, you create microservices by calling seneca.listen
, and
you talk to the services using seneca.client
. In the example, you
are using the default settings for the client and server, which is to
communicate via HTTP over port 10101. Both seneca.client
and
seneca.listen
accept the following parameters:
- port: optional integer; port number,
- host: optional string; host IP address,
- spec: optional object; full specification object.
So long as the client and listen parameters are the same, the two services can communicate. Some examples:
seneca.client( 8080 )
→seneca.listen( 8080 )
seneca.client( 8080, '<IP_ADDRESS>' )
→seneca.listen( 8080, '<IP_ADDRESS>' )
seneca.client( { port:8080, host:'<IP_ADDRESS>' } )
→seneca.listen( { port:8080, host:'<IP_ADDRESS>' } )
Seneca provides you with transport independence because your business logic does not need to know how messages are transported, or which service will get them. This is specified in the service setup code or configuration. In this case, the code in the math.js plugin never changes.
The HTTP transport provides an easy way to integrate with Seneca microservices. But it does have all the overhead of HTTP. Another transport you can use is direct TCP connections. Seneca provides both HTTP and TCP options via the built-in seneca-transport plugin. Let's move to TCP:
seneca.client( { type:'tcp' } )
→seneca.listen( { type:'tcp' } )
The default client/listen configuration sends all messages that the client does not recognize over the listening server. Locally defined patterns are executed locally. It's usually preferable to specific exactly which patterns should be sent to which service, and you can do this using a pin.
Let's put all this together into an example that sends role:math
messages out over TCP on port 30303 (just an arbitrary port), and
executes all other messages locally:
First, the listening service (math-pin-service.js):
require( 'seneca' )()
.use( 'math' )
// listen for role:math messages
// IMPORTANT: must match client
.listen( { type:'tcp', pin:'role:math' } )
Then, the client (math-pin-service.js):
require( 'seneca' )()
// a local pattern
.add( 'say:hello', function( msg, respond ){ respond( null, {text:"Hi!"} ) } )
// send any role:math patterns out over the network
// IMPORTANT: must match listening service
.client( { type:'tcp', pin:'role:math' } )
// executed remotely
.act('role:math,cmd:sum,left:1,right:2',console.log)
// executed locally
.act('say:hello',console.log)
You can use filtered logging to trace the flow of messages. You can
use the command line option --seneca...
to control how Seneca runs,
including the log output generated. Seneca logs have the following
attributes, the most important of which, in order, are:
date-time
: when the log entry occurredseneca-id
: identifier for the Seneca processlevel
: one of DEBUG, INFO, WARN, ERROR, FATALtype
: entry code, such as act, plugin, etcplugin
: plugin name (actions without a plugin have root$)case
: entry case, such as IN, OUT, ADD, etc.action-id/transaction-id
: tracing identifier, stays the same over the networkpin
: the action pattern for this messagemessage
: the inbound or outbound message (truncated if too long)
If you run the above processes with --seneca.log.all
then you'll get all the logs. If you look at the entries, you can see Seneca booting up all the internal plugins.
$ node math-pin-service.js --seneca.log.all
... lots of logs ...
$ node math-pin-client.js --seneca.log.all
... lots of logs ...
It's hard to see the log entries that you care about, the ones relevant to the math plugin. To narrow the output, try this:
$ node math-pin-service.js --seneca.log=plugin:math
20.. 85.. DEBUG plugin math DEFINE {}
20.. 85.. DEBUG plugin math ADD (f3ysr) cmd:sum,role:math sum
20.. 85.. DEBUG plugin math ADD (9mocb) cmd:product,role:math product
20.. 85.. DEBUG plugin math ADD (ydx70) cmd:sum,role:math
20.. 85.. DEBUG plugin math ADD (ka7rj) cmd:product,role:math
20.. 85.. DEBUG plugin math options set {math:{}}
20.. 85.. DEBUG act math IN 2682lsrziy1i/rst61586f7wl cmd:sum,role:math {role:math,cmd:sum,left:1,right:2} ENTRY (ydx70) LISTEN o2.. -
20.. 85.. DEBUG act math IN 1k46jra7rhpd/rst61586f7wl cmd:sum,role:math {role:math,cmd:sum,left:1,right:2} PRIOR;(ydx70) (f3ysr) - - -
20.. 85.. DEBUG act math OUT 1k46jra7rhpd/rst61586f7wl cmd:sum,role:math {answer:3} PRIOR;(ydx70) (f3ysr) - - 0 -
20.. 85.. DEBUG act math OUT 2682lsrziy1i/rst61586f7wl cmd:sum,role:math {answer:3} EXI(ydx70) LISTEN o2.. 1 -
$ node math-pin-client.js --seneca.log=pin:role:math
20.. o2..  DEBUG act remote$ OUT 2682lsrziy1i/rst61586f7wl role:math {answer:3} EXIT CLIENT - 85.. 15 -
null { answer: 3 }
On the listening server, the setting --seneca.log=plugin:math
narrows the log to those entries where the plugin
attribute is
math. You can see the registration of the math plugin, and the
addition of its action patterns. If you remember, you used
seneca.wrap
to override the basic actions with a string to integer
conversion. That means that the role:math actions have two ADD
lines in the logs. You'll also notice that each action gets an
indentifer, of the form (abcde)
. You can use this to find out the
exact action function that gets executed when a message comes in.
When a message comes in, an IN
log entry is created. When the action
function provides a result, an 'OUT' log entry is created. In the
listening server logs above you can see this happening when the
{role:math,cmd:sum,left:1,right:2}
comes in over the network from
the client.
Look carefully at the generated unique action identifers:
2682lsrziy1i/rst61586f7wl
and 1k46jra7rhpd/rst61586f7wl
. These can
be used to trace the flow of messages. The IN
and OUT
lines will
have the same action identifier. Further, the second page, after the
/
, is a transaction identifer. All sub actions triggered by an
initial action have the same transaction identifier, in this case
rst61586f7wl
.
The action identifier persists over the network so that you can trace
message flows when you have many microservices. Let's look at the
client side. The setting --seneca.log=pin:role:math
is another
filter. This time it filters all log entries where the action pattern
contains the specific pin, in this case role:math
.
You can see that the action identifier 2682lsrziy1i/rst61586f7wl
originated on the client, and is passed over the listening server. You
can also see that the client and listening server identifiers, 85..
and o2..
(shortened for clarity) are given in the log entries so
that you can tell which service sent which message.
Also seen in the output of the client is the console.log printing of the results — these are not part of the logs, just ordinary printed output.
There are many ways to configure your microservice communciation architecture. Take a look at the reference links at the end of this guide for more information.
Web Server Integraton
Seneca is not a web framework. But you still need to connect it up to your web service API. Here's the easiest way to do that.
The most important thing to remember is that you don't want to expose
your internal action patterns to the outside world. That's not good
security practice. Instead, define a set of API patterns, say with
property role:api
. Then you can hook them up to your internal
microservices.
Let's look at a simple example using Express. Here's the Express app (app.js):
var seneca = require( 'seneca' )()
.use( 'api' )
.client( { type:'tcp', pin:'role:math' } )
var app = require( 'express' )()
.use( require('body-parser').json() )
.use( seneca.export( 'web' ) )
.listen(3000)
You create a seneca instance, load the api plugin, and then use
seneca.client
to send any role:math
actions out to an external
service. Your Express app is the microservice client.
The integration between Seneca and Express happens in this line:
.use( seneca.export( 'web' ) )
Seneca exports a middleware function that Express can use.
Here's the api plugin (api.js):
module.exports = function api( options ) {
var valid_ops = { sum:'sum', product:'product' }
this.add( 'role:api,path:calculate', function( msg, respond ) {
this.act( 'role:math', {
cmd: valid_ops[msg.operation],
left: msg.left,
right: msg.right,
}, respond )
})
this.add( 'init:api', function( msg, respond ) {
this.act('role:web',{use:{
prefix: '/api',
pin: 'role:api,path:*',
map: {
calculate: { GET:true, suffix:'/:operation' }
}
}}, respond )
})
}
This is a normal Seneca plugin. The role:api,path:calculate
pattern
will be exposed via URL endpoint. The inbound message should have the
property operation
that specifies the calculation to perform: sum
or product
. You can see that the code creates an explicit message
for role:math
, and makes a small attempt to sanitize the input. The
seneca.act
method allows you to build up the message both from a
string (in this case 'role:math'
), and an object, merging the two
together as a convenience.
Never use external input to create an action string. Always explicitly create the internal message. This avoids injection attacks.
The initialisation action makes a call to the pattern role:web
, and
defines the property use
. This is a definition object that defines
a route mapping from URLs to action patterns. It has the properties:
prefix
: the URL prefixpin
: the set of patterns to mapmap
: the list of pin wildcard property values to use as URL endpoints.
Your URL endpoint starts with /api/...
.
The pin is role:math,path:*
. This means map any patterns that have
role = 'api', and where a path property is defined. In this
case, there is only one match: role:api,path:calculate
.
The map has the property calculate corresponding to the value of the path property.
Your URL endpoint starts with /api/calculate/...
.
The calculate property has a subobject that indicates that the HTTP method is GET, and that the URL should have a parameterised suffix (these work just like Express parameters).
Your full URL endpoint is /api/calculate/:operation
.
The remaining message properties are obtained from the URL query string, and from any JSON body submitted with the HTTP request. In this case, we are using GET, so there is no body.
You can re-use the microservice from the previous example. To run the app, start:
$ node math-pin-service.js --seneca.log=plugin:math
... logs as above ...
$ node app.js --seneca.log=plugin:web,plugin:api
... log entries for the web and api plugins ...
To exercise the app, load these URLs in a browser:
http://localhost:3000/api/calculate/sum?left=2&right=3
→ {"answer":5}
http://localhost:3000/api/calculate/product?left=2&right=3
→ {"answer":6}
If you look at the log output, you can see the corresponding action calls. You'll also see the line:
20.. ks.. DEBUG plugin web ACT f7../b8.. role:web http get /api/calculate/:operation
Look for lines like this to see what URL endpoints you are building.
Data Storage
You'll need to persist your data. Especially if you plan to build real-world systems! You can do anything you like inside Seneca actions, and use any kind of database layer. However, why not use the power of pattern matching and microservices to make your life easier?
The pattern matching approach also means you can postpone the debate about microservice data — do services "own" data, do they access a shared database, etc. The pattern matching approach means you can reconfigure your system any which way later on.
Seneca provides a simple data abstraction layer ("ORM"), based on the following operations:
- load: load an entity by identifier
- save: create or update (if you provide an identifier) an entity
- list: list entities matching a simple query
- remove: delete an entity by identifier
The patterns are:
- load:
role:entity,cmd:load,name:<entity-name>
- save:
role:entity,cmd:save,name:<entity-name>
- list:
role:entity,cmd:list,name:<entity-name>
- remove:
role:entity,cmd:remove,name:<entity-name>
A plugin can provide access to a database (say MySQL) by providing implementations of these patterns.
The reason that Seneca supports data persistence as a core feature is that it makes microservice development much easier when data persistence is provided by the same mechanism as everything else: pattern-matched messages.
Using the data persistence patterns directly can become tedious, so
Seneca also provides a more familiar
ActiveRecord-style
interface. To create a record object, you call the seneca.make
method. The record object has methods load$
, save$
, list$
and
remove$
(the trailing $
avoids clashes with data fields). The
data fields are just the object properties.
Let's create and save a simple data entity that stores "product" details:
var seneca = require('seneca')()
var product = seneca.make('product')
product.name = 'Apple'
product.price = 1.99
// sends role:entity,cmd:save,name:product messsage
product.save$( console.log )
Run the file product.js to test this. You'll see the output:
2015-07-10T10:33:18.195Z f7ajdotgpxu9/1436524398186/79031/- INFO hello Seneca/0.6.2/f7ajdotgpxu9/1436524398186/79031/-
null $-/-/product:{id=3i402d;name=Apple;price=1.99}
The response to the role:entity,cmd:save
message is the record
object, which prints itself as
$-/-/product:{id=3i402d;name=Apple;price=1.99}
. You can see that it
auto-generated an identifier for you.
Seneca comes with a built-in data persistence plugin: mem-store. This plugin just stores the data in-memory, and does not actually persist it anywhere. It's very useful for writing fast unit tests!
Because all data operations go via the same set of messages, you can very easily swap databases, at any time. No need to make database choice at the start of your project! Maybe use MongoDB to begin with when your schemas are in development, and switch to Postgres for the final few months before go-live and production.
Let's build a little shop, and integrate it into our existing microservice system. First, here's a simple shop plugin, with some messages for adding products, getting product details, and making a purchase (see shop.js):
module.exports = function( options ) {
this.add( 'role:shop,get:product', function( msg, respond ) {
this.make( 'product' ).load$( msg.id, respond )
})
this.add( 'role:shop,add:product', function( msg, respond ) {
this.make( 'product' ).data$(msg.data).save$(respond)
})
this.add( 'role:shop,cmd:purchase', function( msg, respond ) {
this.make( 'product' ).load$(msg.id, function( err, product ) {
if( err ) return respond( err )
this
.make( 'purchase' )
.data$({
when: Date.now(),
product: product.id,
name: product.name,
price: product.price,
})
.save$( function( err, purchase ) {
if( err ) return respond( err )
this.act('role:shop,info:purchase',{purchase:purchase})
respond(null,purchase)
})
})
})
this.add( 'role:shop,info:purchase', function( msg, respond ) {
this.log.info('purchase',msg.purchase)
respond()
})
}
The role:shop,get:product
pattern retrieves a product from the
"database". You provide the identifier via the id
property. Notice
the respond
callback is simply passed along to the load$
method to
use as its callback.
The role:shop,add:product
pattern adds a new product to the
"database". You provide the data fields via the data
object
property. The data$
method is a shortcut for setting all the data
fields from the provided object (in this case, msg.data
). Again,
respond
is passed along.
The role:shop,cmd:purchase
pattern creates a new row in the
purchase
table (a small part of what would happen in the real world
when you hit the Checkout button). The product
identifier and
details for that transaction are recorded (prices change!). The action
function also emits a role:shop,info:purchase
message, but does
not expect a response. This a common microservice
pattern
— letting the world know that something has happened, but not
caring who gets the message.
Finally, you provide a default implementation for the
role:shop,info:purchase
message. This is useful for debugging and
unit testing. In the example code, you can see that it uses the
seneca.log.info
method to log the purchase event. The seneca.log
object provides a method for each of the log levels: debug
, info
,
warn
, error
, fatal
. These methods also annotate your log entries
with the name of your plugin.
Let's create simple unit test for this plugin. Writing unit tests for Seneca plugins is very easy — verify that inbound messages generate the right responses. Here's the unit test code (shop-test.js):
var assert = require('assert')
var seneca = require('seneca')()
.use('shop')
// uncomment to send messages to the shop-stats service
// .client({port:9003,pin:'role:shop,info:purchase'})
.error( assert.fail )
add_product()
function add_product() {
seneca.act(
'role:shop,add:product,data:{name:Apple,price:1.99}',
function( err, save_apple ) {
this.act(
'role:shop,get:product', {id:save_apple.id},
function( err, load_apple ) {
assert.equal( load_apple.name, save_apple.name )
do_purchase( load_apple )
})
})
}
function do_purchase( apple ) {
seneca.act(
'role:shop,cmd:purchase',{id:apple.id},
function( err, purchase) {
assert.equal( purchase.product, apple.id )
}
)
}
This code uses the built-in Node.js
assert module. It loads the
shop
plugin, and then exercises the role:shop
messages. If you run
the test file, you'll see the output:
$ node shop-test.js
20.. h3.. INFO hello Seneca/0.6.2/h3s0y3fckpde/1436528824335/79305/-
20.. h3.. INFO plugin shop ACT gj.. info:purchase,role:shop purchase {when:1436528824554,product:90vzcc,name:Apple,price:1.99,id:ntzosx}
This output includes the log entry that you created with
seneca.log.info
in the implementation of the
role:shop,info:purchase
action.
Let's run a separate service to capture the purchase message events. For the sake of example we'll just count the number of purchases per product. Here's the shop-stats.js microservice:
var stats = {}
require('seneca')()
.add('role:shop,info:purchase',function( msg, respond ) {
var product_name = msg.purchase.name
stats[product_name] = stats[product_name] || 0
stats[product_name]++
console.log(stats)
respond()
})
.listen({port:9003,pin:'role:shop,info:purchase'})
This service listens on port 9003, and prints out a product purchase
statistics report every time a new purchase is made. Notice that you
are providing an implementation of role:shop,info:purchase
.
To see this in action, uncomment the line in shop-test.js:
// uncomment to send messages to the shop-stats service
// .client({port:9003,pin:'role:shop,info:purchase'})
And then run both:
$ node shop-test.js
20.. j0.. INFO hello Seneca/0.6.2/j0z5x7zz2r0l/1436529606670/79365/-
20.. j0.. INFO client {port:9003,pin:role:shop,info:purchase}
$ node shop-stats.js
20.. wb.. INFO hello Seneca/0.6.2/wbxim58g0vgd/1436529589836/79362/-
20.. wb.. INFO listen {port:9003,pin:role:shop,info:purchase}
{ Apple: 1 }
Bringing it Altogether
You're going to run four services. In the real world, you'd use something like Docker to keep yourself sane. For the purposes of this example, you'll run everything in the terminal as bare processes.
The services are:
- shop-stats.js: collect shop statistics
- shop-service.js: provide shop functionality
- math-pin-service.js: provide math functionality (as above)
- app-all.js: web server
The services shop-stats
and math-pin-service
are the same as
before, so you can spin them up right away:
$ node math-pin-service.js --seneca.log.all
$ node shop-stats.js --seneca.log.all
In this example, we're using --seneca.log.all
to log at the highest
level of detail. There's a lot of output. Look out for the act
lines, and the IN
and OUT
cases, and you can trace the message
flows.
We need a shop-service.js:
require( 'seneca' )()
.use( 'shop' )
.listen( { port:9002, pin:'role:shop' } )
.client( { port:9003, pin:'role:shop,info:purchase' } )
This service listens for inbound role:shop
messages, but sends
any role:shop,info:purchase
messages out onto the network. Seneca
lets you mix and match client
and listen
configurations. Remember,
the client and listen pins must match.
In this configuration, we've put the shop-service
on local port
9002, and the shop-stats
service on local port 9003. In production,
you might use a message
bus, or have
multiple
clients, or an
overlay network, or even service
discovery to configure the port and host.
Start the shop
service:
$ node shop-service.js --seneca.log.all
The shop functionality is exposed via the URL endpoints
/api/shop/get
and /api/shop/purchase
. We need to add these to the
api plugin (api-all.js):
...
this.add( 'role:api,path:shop', function( msg, respond ) {
var shopmsg = { role:'shop', id:msg.id }
if( 'get' == msg.operation ) shopmsg.get = 'product'
if( 'purchase' == msg.operation ) shopmsg.cmd = 'purchase'
this.act( shopmsg, respond )
})
this.add( 'init:api', function( msg, respond ) {
...
this.act('role:web',{use:{
prefix: '/api',
pin: 'role:api,path:*',
map: {
shop: { GET:true, POST:true, suffix:'/:operation' },
}
}})
respond()
})
Finally, we need to update the web server to send role:shop
messages
to the shop-service
(app-all.js):
var seneca = require( 'seneca' )()
.use( 'api-all' )
.client( { type:'tcp', pin:'role:math' } )
.client( { port:9002, pin:'role:shop' } )
var app = require( 'express' )()
.use( require('body-parser').json() )
.use( seneca.export( 'web' ) )
.listen(3000)
// create a dummy product
seneca.act(
'role:shop,add:product',{data:{name:'Apple',price:1.99}},
console.log
)
We've also added a dummy product for testing. When you run the web service with:
$ node app-all.js --seneca.log.all
the last line of the output will be something like:
null $-/-/product:{id=mbm07t;name=Apple;price=1.99}
Copy the product identifier, in this case mbm07t
. You can use this
to exercise the system.
First, get the product details:
http://localhost:3000/api/shop/get?id=mbm07t
→ {"name":"Apple","price":1.99,"id":"mbm07t"}
Then, make a purchase:
$ curl -d '{"id":"mbm07t"}' -H "content-type:application/json" http://localhost:3000/api/shop/purchase
{"when":1436536799159,"product":"mbm07t","name":"Apple","price":1.99,"id":"ny09dx"}
You need to do this using curl
on the command line, as it has to be
a HTTP POST request. Also note the need for the corrent content type
header: application/json
.
Look at the logging output of all services. You'll be able to trace the action identifiers and transaction identifiers accross all the services.
Don't forget that the math service still works:
http://localhost:3000/api/calculate/sum?left=2&right=3
→ {"answer":5}
The math and shop services can be changed, updated, deployed, or even removed independently.
Changes to one service do not affect the others. This is how microservices give you continuous delivery.
Next Steps
For more information on the functionality that Seneca provides, read these articles:
For more information about inter-service communication, read the seneca-transport documentation, and also read the salestax examples.
For an example of a real system, read the nodezoo.com articles.
There are also some conference talks on Seneca.
If you have questions, you can:
- Tweet to @senecajs,
- Post a github issue,
- Start a conversation.
Good luck, and stay micro!


This sample contains an .mswallet, wallet item package. In this example, we'll import that package, creating a new iem in Wallet as a result.


-
Related articles
-
Free tools
-
Mobile Security for Android
- Scans apps for malware
- Remote lock and wipe
- Detection from the cloud
- Completely free
- More...
-
Antivirus for Mac
- Clears viruses and Trojans
- Stops all threats, even new ones
- Installs easily and runs quietly
- Completely free
- More...
-
Virus Removal Tool
- Removes viruses and rootkits
- Windows XP SP2 and up
- Works with existing antivirus
- Completely free
- More...
-
UTM Home Edition
- Fully-equipped UTM
- Network, web, mail and VPN
- Protects 50 IP addresses
- Completely free
- More...
-
UTM Essential Firewall
- Network security for business
- No user/IP limitations
- Upgrade to full UTM
- Completely free
- More...
-
-
Hot this week
Podcast
SSCC 191 - Live in Ljubljana [PODCAST]
Chester is on the road again, this time to present at a conference in Slovenia.
So this episode of the Chet Chat comes to you from an al fresco café in downtown Ljubljana...
SSCC 190 - The CeBIT 2015 edition [PODCAST]
Recorded right on the Sophos booth at the CeBIT show in Hannover, Germany.
Here's the Fifth Anniversary edition of our weekly podcast...enjoy!
SSCC 189 - Hey, is that your CPU on fire? [PODCAST]
Join Sophos experts Chester Wisniewski and Paul Ducklin for our weekly security podcast.
Sharp, witty and educational, as usual (if we do say so ourselves)...enjoy!
SSCC 188 - Live long and prosper [PODCAST]
Join our experts Paul Ducklin and Chester Wisniewski as they take you on a 15-minute tour of the hot topics in security this week.
SSCC 187 - The cryptography edition [PODCAST]
Sophos expert John Shier sits in for regular presenter Chester Wisniewski in this episode.
John and Paul Ducklin dissect the latest security issues, which were dominated this week by some thorny matters of cryptography.
SSCC 186 - Just how firm is your firmware? [PODCAST]
Here's the latest episode of our weekly security podcast - from firmware and Firefox to Project Zero and Lightning conductors.
Chet and Duck are in fine fettle as usual...enjoy!
SSCC 185 - "I have a number for you: Eighty Million" [PODCAST]
Our weekly "Chet Chat" podcast is carefully prepared to fit into a quarter-hour, so it is clear and concise as well as being witty and amusing.
Enjoy...
SSCC 182 - What would the Pirate Party do? [PODCAST]
This week's episode of our quarter-hour security podcast.
Entertaining as well as accurate and educational - why not give it a listen?
SSCC 181 - The Security Duel: "Bug reports at 15 paces" [PODCAST]
Microsoft vs. Google - Google vs. Users - Hackers vs. US Army - the fight is on in the latest episode of our weekly security podcast!
Enjoy...
SSCC 180 - Surely zero-days come from cybercrooks, not from Silicon Valley? [PODCAST]
Enjoy the first 2015 episode of our popular weekly security podcast.
In this episode: zero-day politics, leaky security features, Bitcoin news, and a shout out to our New Year #sophospuzzle winners!
SSCC 179 - What kind of a name is "Lizard Squad"? [PODCAST]
Here's the latest episode of our regular security podcast.
This week: phishing, spamming, zombification, SCADA and the Internet of Things, and the curiously named cybervandals that go by "Lizard Squad."
SSCC 178 - Are we there yet? [PODCAST]
Here's the latest episode of our weekly security podcast.
Enjoy...and "Happy Holidays," whether you're away on vacation yourself, or a sysadmin enjoying the time when everyone else is on vacation!
SSCC 175 - "My, what an ENORMOUS malware infection you have!" [PODCAST]
Here's the latest episode of our weekly security podcast.
For your listening pleasure - the news you can use!
Sophos Techknow - Dealing with Ransomware [PODCAST]
No type of malware seems to get people's blood boiling quite as much as ransomware.
Learn how to protect yourself in this episode of our Sophos Techknow podcast series...
SSCC 174 - Who says law enforcement isn't tackling cybercrime? [PODCAST]
Here's the latest epsiode of our weekly Chet Chat podcast, for your listening pleasure.
From a carder ring that got busted to a spamming system that ran amuck, let yourself be amused and educated at the same time...
SSCC 173 - Lest we forget [PODCAST]
Here's the latest episode of our weekly "Chet Chat" security podast.
Chet and Duck take on the week's news in their inimitable way...enjoy!


The GUI Designer allows to arrange the graphical user interfaces (GUI) of those components that have one. These GUI Elements will then be shown on the ARE GUI. GUI elements are drawn and deleted automatically when a component with a GUI is added to or deleted from the model. Within the GUI Designer the size and the position of the GUI Elements can be changed by drag and drop.
Keyboard users can enter keyboard mode by hitting the Enter key when GUI Designer is selected. Then the user can navigate the elements by using the arrow keys. Shift-arrow keys moves the selected element, Alt-arrow keys resizes the selected element.