Availability : I'm usually available for development work on anything interesting (pretty much any programming language), as long as I can do it from home. I'm also prepared to write about virtually anything, for reasonable recompense. Likewise I'll be happy to speak anywhere on just about anything, assuming expenses are covered. The best way to contact me is by email<EMAIL_ADDRESS>Current Activities
- Programming, R&D
Ongoing activities include experimenting with an agile approach to Semantic Web application development. Near the top of my to-do list is Pragmatron, the umbrella name for a set of Personal Knowledge Management tools, which I hope before long will be able to look after my to-do lists. I recently got involved in the Gargonza Experiment, which aims to produce cool demos of Semantic Web technologies, particularly using the SPARQL Query Language for RDF. A part of the Pragmatron project was suitable for this (RSS/Atom aggregation bits), so I've recently been concentrating on that with the working title SparqlSphere. IdeaGraph is a long-term project to develop a desktop Personal Knowledge Management application. This is currently on pause while I'm experimenting with server-side components. Associated with these projects I have various RDF/OWL vocabularies (at various stages of incompleteness) for various specific purposes, such as online Reviews and Project Management. I've also done some work on an OWL Ontology for Atom, and some stuff for putting pets (like Basil) on the Semantic Web.
Once these parts are at a reasonably well-developed stage, I want to spend time exploring techniques for combining Semantic Web and machine learning technologies to autodiscover connections between ideas. This isn't much more than a few loose ideas at present (with working titles like "SemText" and "Serendipity Server"), partly because a lot of experimentation will be needed to see what works. Tools suitable for fusing logic-based inference and statistical techniques in the (Semantic) Web environment aren't yet available, but are on the to-do list.
At any given time I've usually got half-a-dozen different little hacks in progress. As time permits I've been putting together a graph-oriented RDF API, "Sambuca" the target language is Smalltalk (for Squeak), though I somehow find myself hacking in Python at the moment. Ah well, port later. At some point I'm hoping to do some experimentation around WordNet for the WordNet Task Force, although I'm still trying to get up to speed on the background. I'm always in the process of reorganising my online material. The main thing outstanding is to collate links to hacks and written material - a while ago I made a start with Delivered and In Progress, but recently most hacky things have been posted on my weblog but not catalogued elsewhere. To-do.
When funds are low I generally do contract development/programming and consultancy work.
- Technical Writing
Recently finished Beginning RSS and Atom Programming for Wrox/Wiley, co-authored with Andrew Watt (see also: Published Work).
I've usually got a handful of short pieces on the go, with the hope I can get paid for one or two of them. Output includes Extending RSS and Creating an SVG Wiki (both at xml.com) and The Legend of View Source (on this site). I have worked on several other books. I was recently interviewed on RSS in the Enterprise.
- Community Work
I'm involved in various online collaborative projects notably:
|
Group
|
Activity
|
| The Gargonza Experiment | - SPARQL Protocol and RDF Query Language testbed/showcases |
| Semantic Web Interest Group | - community support and development of SW technologies |
| Blue Oxen Associates | - think tank on collaboration and augmenting knowledge work |
| Atom Working Group | - specification of an open syndication format and protocol |
| Yahoo! Media RSS | - development of extensions to RSS to support media |
| Podcasting Specification WG | - specification of assorted media syndication formats and protocols |
I had the pleasure of witnessing the transformation of the RDF Interest Group into the Semantic Web Interest Group at the W3C plenary in Cannes, France 2004, where I also gave a lightning talk. I gave an invited talk at SWAP2004 (Italian Workshop on Semantic Web Applications and Perspectives, Ancona University) and am on the programme commitee for SWAP2005. I was also on the programme commitee for SFSW2005 (Workshop on Scripting for the Semantic Web, European Semantic Web Conference 2005).
- Weblogs
I've had a personal blog for several years. I tend to post a lot on Semantic Web-related matters, and the blog is one of those aggregated on Planet RDF, despite (or maybe because of) the numerous cat photos. I maintain the Finally Atom weblog, keeping track of developments in the Atom syndication format/protocol project. I'm the primary contributor to a professional blog focussing on Grid computing: GridReporter.com. I'm a very occasional contributor to Lambda the Ultimate the programming languages weblog (usually when a new RDF serialization appears).
Personal Stuff
I live in northern Italy with my wife Caroline. We're originally from the Peak District in northern England, (she from Bakewell, me from Tidza ). We lived in Sri Lanka for a while before settling here. We've got a dog and six cats (a lot I know, but they adopted us).
You'll have gathered I'm a developer, tech author and consultant, on forward-looking Web technologies. I enjoy this stuff.
I also enjoy making electronic music, but haven't done any for a long time (I was a founder member of Nocturnal Emissions, way back). I very much like carving wood, and since being near the Carrera marble quarries (as in the photo above) I've started trying stone. I'll have another crack at oil painting when time permits, it seems you need practice to get beyond blobby trees. I'm not remotely athletic but did try skiing once, a lot of fun despite the bruises. We usually just plod up hills on foot, being dragged by Basil dog.
- Some History
My first computer was one of these:
I've been interested in knowledge representation and machine learning for many years and have worked for several years on network technologies. I'm delighted to see these fields are now converging before my very eyes, in the form of the Web, soon to be formerly known as the Semantic Web. I got interested in these particular technologies while trying to build a smarter search engine, quite a while ago now.
I'm active on the Web. The earliest material of mine still live I believe is this from around 1996. Most things pass through the blog these days (my earliest bloglike post is probably this postcard from around 1999/2000).
I haven't really got any paper qualifications, though I had 2 years studying electronics for the music industry (HND level) and a one-year degree course at Sheffield Hallam University on Engineering Information Technology, really enjoying the digital signals material but flunking the finals on analogue circuit theory. I've also done some of a part-time MSc in Computing for Commerce and Industry with the Open University, although that's become totally redundant with the work I've been doing since.
My last full-time day job was about 5 years ago at the University of Derby's Buxton Campus (High Peak College), where for around 4 years my primary responsibility was looking after a network (Netware, NT and my pet Linux box) serving several hundred staff and several thousand students. The IT department was a very small team, so I was also on call for end-user support (I still cringe when the phone rings). At one point I had the pleasure of redesigning the whole server configuration essentially single-handedly, I also put together the College's first Intranet and was behind their initial presence on the Web. Around the same period I was an invited speaker at the MumJava Conference in Mumbai (Bombay), India. I also did a little teaching at the College, basic Web stuff and Java.
Published Work
I wrote significant chunks of these books :
- Beginning RSS and Atom Programming (lead author, Spring 2005)
- Beginning XML
- SVG Unleashed
- XML Application Development with MSXML 4.0
- Professional XML Meta Data
- Professional Java Mobile Programming
- Professional Java Data
- Professional JSP
- Professional Java Server Programming (Spring 1999)
I've tech reviewed a lot more.
Invited article for the AIS SIGSEMIS Bulletin (PDF) Association for Information Systems, Special Interest Group on Semantic Web and Information Systems, Vol. 1, Issue 2, July 2004 : "The Missing Webs" (local copy, HTML), outlining some of the pieces missing from the current WWW and why I think the W3C's Sematic Web initiative is probably the best way forward to a better Web.
I've done a couple of short pieces for O'Reilly's xml.com : Extending RSS and Creating an SVG Wiki
In the days before the Web I had several articles published in the magazine "Electronics World & Wireless World" - on or roundabout non-linear circuitry. These included a write-up on how easy it was for circuits to get chaotic, and a bat detector. Two of these articles appeared as the cover feature of the magazine.
For totally up-to-date info, check the weblog.
@@TODO hCard, vCard? GRDDLable FOAF/XFN
1999-2005 Danny Ayers


尝试点击常按列表元素看console
tey to click the list-item and open console see what happen
尝试常按h1元素内容看console
try click h1 see the console.


enum class TimeRange
The time frame for which attribute affinities are computed.
id
- the Spotify id of the time frame
(common, js, jvm)
LONG_TERM |
Calculated from several years of data and including all new data as it becomes available |
(common, js, jvm)
MEDIUM_TERM |
Approximately last 6 months |
(common, js, jvm)
SHORT_TERM |
Approximately last 4 weeks |
(common, js, jvm)
id |
the Spotify id of the time frame val id: String |
(common, js, jvm)
toString |
fun toString(): String |
(common, js, jvm)
LONG_TERM |
Calculated from several years of data and including all new data as it becomes available |
(common, js, jvm)
MEDIUM_TERM |
Approximately last 6 months |
(common, js, jvm)
SHORT_TERM |
Approximately last 4 weeks |


Game boy projects
Gameboy screen
Fixing a gameboy
Repair - Gameboy battery upgrade
Repair - Sweaty palms ultra oxidized Gameboy
Gameboy housing a DS
Gameboy clock
Gameboy homebrew cartridge
Gameboy guts and programming
GbReaper
Image found on DeviantArt
http://darklyon.deviantart.com/art/Gameboy-24877611


Heroku uses the Progressive Enhancement style of web design
Heroku uses HTML and CSS design that can be used and incorporated into different versions of different web browsers. Although these older browsers will not see the more sleek and 'prettier' versions of the website. They will be able to atleast see the website and use its features.


|
Space Station Rescue Souls
|
Represents non-constructable registry. More...
#include <Registry.h>
Static Public Member Functions | |
| static Registry & | get () |
| Retrieve a reference to our singleton registry. More... | |
Static Private Attributes | |
| static std::unique_ptr< Registry > | s_registry = nullptr |
| The only pointer to our registry instance. More... | |
Represents non-constructable registry.
Contains a single static instance of our registry to make access to the registry easier as well as utilise the singleton pattern to garantee only one registry is ever created.
A registry will store all our entities and their components in highly optimised sets to allow for fast retrieval and iteration of several entities and different combinations of componenents.
Definition at line 20 of file Registry.h.
|
static |
Retrieve a reference to our singleton registry.
Checks if our instance app::Reg::s_registry is instanciated, and instanciate it if it isn't, effectevily lazy loading our registry and retrieve a reference to it.
Definition at line 21 of file Registry.cpp.
|
staticprivate |
The only pointer to our registry instance.
Keeps ownership of our registry instance so that any deletion is done automatically via the unique pointers destructor.
Definition at line 35 of file Registry.h.


NELLY & MONSIEUR ARNAUD A film review by James Berardinelli Copyright 1996 James Berardinelli
RATING (0 TO 10): 7.5 Alternative Scale: *** out of ****
France, 1995 U.S. Release Date: beginning 4/96 (limited) Running Length: 1:46 MPAA Classification: No MPAA Rating (Mature themes) Theatrical Aspect Ratio: 1.66:1
Cast: Emmanuelle Beart, Michel Serrault, Jean-Hugues Anglade, Claire Nadeau, Francoise Brion, Michele Laroque, Michel Lonsdale, Charles Berling, Jean-Pierre Lorit Director: Claude Sautet Producer: Alain Sarde Screenplay: Claude Sautet, Jacques Fieschi, and Yves Ulmann Cinematography: Jean-Francois Robin Music: Philippe Sarde
There's a line near the end of NELLY & MONSIEUR ARNAUD that, for me, sums up the entire movie: "We all want love, but when we find it, we pull back. It scares us." Claude Sautet's feature is about the mysteries of the human heart and mind, and, while it doesn't pack the same emotional power as his 1992 film, UN COEUR EN HIVER, it deals with many of the same subjects in an equally intelligent manner.
Some complain that Sautet's films are too intellectual, but, like Eric Rohmer, the film maker uses conversation to delve deeply into the core of his characters. You don't start out feeling much for the men and women who populate his films, but, by the final reel, you empathize with them all. In NELLY & MONSIEUR ARNAUD, Sautet injects the finale with a bitter dash of irony -- it's an unexpected twist that underscores his main theme.
Nelly (the enchanting Emmanuelle Beart) is trapped in a dying marriage with her husband, Jerome (Charles Berling). While he vegetates at home watching TV, she goes out and gets odd jobs, trying to earn enough money to dig them out of a 30,000-franc financial hole. One day, Nelly's friend, Jacqueline (Claire Nadeau), introduces her to an elderly acquaintance, Pierre Arnaud (Michel Serrault). Nelly and M. Arnaud talk for a while, and he offers her both a loan to cover her debts and a job typing and editing his memoirs. She accepts. Soon, the pair are spending several hours a day together, unwittingly becoming deeply involved in each other's lives as their outside relationships grow pale.
NELLY & MONSIEUR ARNAUD is as close to a perfectly-acted motion picture as is likely to be found. Emmanuelle Beart (MANON OF THE SPRING, MISSION IMPOSSIBLE), one of today's most attractive and accomplished French actresses, emotes as much with her eyes and gestures as with her words.  Her co-star, Michel Serrault, matches her scene-for-scene with a towering performance of often restrained, but occasionally unleashed, passion. Jean-Hughes Anglade (as Arnaud's editor and Nelly's lover) and Claire Nadeau are solid in supporting roles.
It's easy to see NELLY & MONSIEUR ARNAUD as a May/December romance, but it's more than that. It's about longing for the forbidden fruit, shielding oneself from disappointment, and holding back from the thing most wanted. Sometimes love involves passion; on other occasions, the two are separate. As in Kieslowski's RED, the relationship between the beautiful, young woman and the aging ex-judge runs far deeper than a term like "friendship" can adequately describe.
Sautet's film was nominated for a ton of Cesar awards. It won two: best director for Sautet and best actor for Serrault. Both are deserved.  When Nelly confesses to M. Arnaud that he's a part of her life now, she speaks for them both. Like a couple that has been together for years, they confide in each other, quarrel, and reconcile. They are well-matched in every way except age.
There are times when NELLY & MONSIEUR ARNAUD seems a little sterile, and several secondary relationships don't consistently engage the audience. But when Beart and Serrault are together on screen, they make the most unusual kind of magic. These are two great actors giving top-notch performances, and the restrained romance they project is fascinating. It's this quality, more than any other, that makes NELLY & MONSIEUR ARNAUD a rewarding motion picture.
- James Berardinelli e-mail<EMAIL_ADDRESS>web: http://www.cybernex.net/~berardin
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


Not long ago, response times in the seconds were considered appropriate. Browser refreshes were the norm in web applications. Systems would go down for hours of maintenance, or even be rebooted nightly, and this was ok because people only expected the systems to be up during business hours. Applications didn't have to scale because they didn't have big user bases. And the complexity requirements put on web applications meant that typical requests could easily be handled by a thread per request model.
Things are changing though. People expect web applications to react instantly. They expect them to be up all the time, while the applications are moving into the cloud, where failures are not exceptional, but rather are the norm, and so applications need to react to failure. Load on a web application can peak unpredictably, to be many orders of magnitude greater than normal, and so applications need to react to load and scale out. The complexity of business requirements means that in order to respond quickly to requests, things must be processed in parallel, reacting to events rather than waiting so as to utilise resources as efficiently as possible.
This application is an example of how to implement the tenets of the Reactive Manifesto using the Typesafe Reactive Platform.
It uses Play, combined with the latest in client side technologies to implement a reactive user interface. It uses Akka to provide horizontally scalable and resilient message passing and data management.
The tutorial starts by becoming familiar with the application, walking through its code and then enhancing the code. Toward the end there is a discussion on how reactive applications can be managed in production using our ConductR product to demonstrate.
Before jumping into the code, let's see the app in action. Go to the Run tab, and start the application if it's not already started. Then visit it at: http://localhost:9000.
You will be presented with a screen asking for your email address. After entering it and submitting, you should see a map, and you should be able to find yourself on that map (this may take a short amount of time due to the way data flows through summary regions in the system, the further you zoom out the less realtime the app gets).
If you zoom in on North Carolina, you should see some bots walking around. These bots are simulating other users, the data used to generate their paths is taken from hiking trail data that was grabbed from HikeWNC.
The system can be broadly divided into three parts. The first part is the client side app. This is written in CoffeeScript, and runs in the browser. The second part is the web front end, this is a Play application that serves web requests coming in. The third part is the Akka backend, which manages the distribution of data across backend nodes, and the publishing and subscribing of events.
In the demo you're seeing now, the Play web front end and the Akka backend are running as one application, but in a production scenario, they would be run separately, allowing fine grained control of resources between the front and backend.
We also recommend that you factor out the two types of application into separate sbt modules, or even their own projects. They are presented here under the one project for the convenience of this tutorial. Factoring them out into their own modules/projects then allows them to be released/deployed independently of each other.
The client talks to the web front end using WebSockets:
All the communication above is fire and forget, after sending a user moved event, the client doesn't need anything in response, after sending a viewing area message, the client might get many messages, or maybe none at all, depending on whether there are any users in that area, and after the server sends position updates, it expects nothing in return from the client.
This differs from many traditional client server where clients make a request and expect a response. In a reactive application, much of the communication will not be request/response based, because the way reactive applications are designed is that data flows to consumers as it becomes available, and consumers of the data react to it, they don't ask for it.
For this reason, WebSockets makes for a perfect transport for client server communication in a reactive application, since it allows events to be passed with low overhead, not needing to wait for a response, and facilitates reacting to events from the server.
Before explaining the backend interface, we need to have a short lesson in geo based systems. A naive way to create the reactive maps application would be to send all data from all users to every connected user. This might work if there are only 10 users connected, or maybe even 100. At 1000, each user is going to be downloading megabytes of updates per second - it is not going to scale.
To manage this, we break the earth up into regions. There are many different ways to do this, but in our app we're using the simplest to understand, we flatten the map out into a rectangle, and then divide it into many smaller rectangles. How many rectangles is configurable, but we have defaulted this to 16 million. Because the earth is not a reactangle, but is actually a sphere, these rectangles don't all cover the same area, at the equator each one is a few kms wide, at the poles, each rectangle is only a few metres wide. But each rectangle is a constant number of degrees in longitude wide, and degrees in latitude high, so transforming latitude and longitude coordinates to regions is therefore a straightforward equation.
The web front end talks to the backend using Akka clustering:
Actors in an Akka cluster may talk to each other without knowing whether they are on the same node or different nodes. In the above diagram, when a frontend node receives a position update from the client, the region responsible for that position may be on the same node, or may be on a different node. The web frontend doesn't need to worry, all it needs to know is which region to send to, and Akka will work out how to get the message to the right node.
Akka distributed PubSub messaging is used to publish location updates to the frontend. When the web frontend gets a new viewing area from the client, it works out which regions cover that viewing area, and then subscribes to updates from each of those regions. Whether those regions are on the same node or on different nodes is transparent, Akka ensures that the right messages get to the right subscribers on the right nodes. When a region has a new update to publish, it pushes it to the PubSub manager, which then pushes the messages to the right subscribers on the right nodes.
Note that Akka's distributed PubSub messaging makes no guarantees in terms of delivering a message. However for this use-case, if the occasional publication of a location update is missed then we need not be concerned as the next update will bring the client up to date. Note that for situations where delivery becomes more important then look into Akka's distributed data library.
Finally, regions get summarised into summary regions, and these summary regions are used so that clients that are viewing large areas at once aren't consuming too much data. Lowest level regions and higher level summary regions send updates to their higher level summary region, which aggregates and publishes the information. When the client requests a viewing area that contains too many regions, it subscribes instead to updates from summary regions.
Now that we've got a broad overview of the system architecture, let's start looking at the code. We'll start off with tracing the code through from what happens when a user's GPS enabled device sends an update.
The entry point to this event flow is in gps.coffee. This file contains a class for for handling the GPS integration of the app. It uses the HTML5 Geolocation API to watch for location updates from the browser.
The first thing you'll find in this, and most other CoffeeScript files in this app, is a call to
define
. This is a RequireJS call, used to
define a module. RequireJS allows JavaScript to be developed in a modular way, which is important for rich
client side apps that heavily use JavaScript like this one. At the bottom of the file you can see a return
statement returning the Gps
class that we've declared, this means anything that imports our module
will get that class back.
The bulk of the code in this file is actually dealing with ensuring that neither too few, nor too many location
updates are sent to the server. It ensures that a location update is sent at least every 10 seconds, but no
more frequently than every 2 seconds. The most pertinent code that we're interested in now though is the
navigator.geolocation.watchPosition(...)
call, this is the HTML5 Geolocation API call to watch for
GPS updates, and also the @ws.send(...)
call, this sends a user-moved
event as JSON
through the WebSocket, with the users current position.
The position
field of this event is formatted using the GeoJSON standard,
which you'll soon see is used throughout the application.
So we can now see how location updates are obtained from the browser. But where does the WebSocket come from
that it's getting sent down? You'll see that the constructor of the Gps
class accepts the WebSocket as a
parameter. This constructor is called from the
mainPage.coffee module. In
this module, you can see that in the define
call it declares a few dependencies, one being the
./gps
module that we just looked at.
Scroll down to the connect
method, and you'll see the following code:
@ws = new WebSocket(jsRoutes.controllers.Application.stream(email).webSocketURL())
This is the code that creates the WebSocket, and a few lines below that, in the onopen
callback,
you can see where we are passing the WebSocket to the Gps
constructor. The URL for the WebSocket
is generated from a JavaScript reverse route which is defined in
main.scala.html.
Open main.scala.html. This is the template where that reverse route is defined. Play has a configuration file called routes, this file contains all the configuration for how incoming requests are routed to their corresponding actions in Play. In addition to providing this forward routing, Play also generates a reverse router, that code such as this template can call, and it will return the URL that can be used to reach that route. This means that your path information is kept in one place - in your routes file, and everything else in your application can depend on it. Play also provides the same mechanism for client-side code so that you can assemble url and request methods by applying a function and giving it input parameters which will be used to populate placeholder values, such as path variables and query parameters.
In the routes file, you can see that the /stream/:email
path is routed to
controllers.Application.stream
, so the JavaScript reverse router call
jsRoutes.controllers.Application.stream(email).webSocketURL
will return us that path.
You can read more about routing in Play Framework here. You can also read more about JavaScript routing here.
In the routes file, we saw how the WebSocket route was
defined, and how it gets routed to the controllers.Application.stream
method. Let's open that
class now, Application.scala.
Looking at the stream
method, the first thing to notice is that it is declared to be a WebSocket
action that works with ClientEvent
messages. These messages are defined in
ClientConnection.scala, we can see our
three types of messages there, UserMoved
, ViewingArea
and UserPositions
.
Below the declaration of the message types, we can see formats for serialising these events to and from JSON, and for formatting the WebSocket frames. We won't go into too much detail here, you can read more about Play's JSON support here.
You can see back in Application.scala
that we have told Play to use an actor to handle the WebSocket. This means our deserialized
ClientEvent
messages are sent to this actor, while when this actor sends ClientEvent
messages to the passed in upstream actor, these messages will be serialized and sent over the WebSocket
to the client.
Back in ClientConnection.scala, beneath the event message types, you will find the actual actor that handles the client connection. The receive method shows the handling of the different message types that this actor will receive. We'll focus on just one of these message types.
Each time a UserMoved
event is received, it's translated to a UserPosition
object,
and sent to the RegionManagerClient.
This class is responsible for sending user position updates to the right node for the region that that position
lives in. You can see in that class that the first thing it does is look up the regionId, and then it creates
a UserPositionUpdate
message, and sends that message to a router.
But how does that router get it to the right node? The configuration for that router can be found in
application.conf. Scrolling down to the
configuration in the akka
section, you'll see this:
/regionManagerClient/router {
router = consistent-hashing
nr-of-instances = 1000
cluster {
enabled = on
routees-path = "/user/regionManager"
allow-local-routees = on
use-role = "backend-region"
}
}
The routing to the node responsible for a region is done with a cluster aware consistent hashing router. The region identifier is used as key for the consistent hashing. This means that updates for a region are routed to the backend node responsible for that region. When the number of nodes in the cluster changes the responsibility for a region may change. In this application the states of the regions don't have to be migrated when this happens. Updates for some regions are routed to a new backend node and old data will expire. For a short period the region points (counts of users) might be slightly inaccurate, but that is acceptable for this application.
The hash code used to route messages is specified by the ConsistentHashable
interface, you can see
that the UpdateUserPosition message
implements this interface, and defines the hash key to be the region ID that the update is for.
If you're interested in learning the full details of Akka routing and how to configure it, you can read about Routing and Cluster Aware Routers in the Akka documentation.
We've seen how the web frontend receives GPS user position events and then routes them to the right backend node in the Akka cluster. Now let's find out what happens with the events when it reaches the backend node.
In the configuration for the router that we saw before, we could see this config item defined:
routees-path = "/user/regionManager"
/user
is the namespace for all user defined actors (as opposed to actors defined by the system
itself), so this says that the messages get sent to a user defined actor called regionManager
,
which is implemented by the RegionManager
class.
The region manager is responsible for managing all the regions that belong on that node. If it gets some data for a region, and an actor for that region doesn't exist yet, it creates it. Once it has ensured that an actor exists for that region, then it sends the user position to that region.
The actor class that represents a region is called
Region. This class has a map called
activeUsers
, and when it receives the user position, it adds that users position to the map.
A client displays a section of the map, which is decorated with live markers of other users in that area. How are those user positions published to the client?
When the user zooms or changes map position the client sends a ViewingArea event to the server, which ends up in PositionSubscriber via the controller. The PositionSubscriber works out which regions cover that viewing area, and then subscribes to updates from each of those regions.
The published updates of user positions come from the backend Region actors. The thing that ties the publisher and subscriber together is the named topic, which in this case is the region id.
In a similar way the PositionSubscriber may decide to subscribe to summary regions, and then the published region points comes from the SummaryRegion actors.
The publish/subscribe mechanism in Akka is a registry of subscribers that is replicated to members in the cluster. There is no central hub or broker. When publishing a message to a named topic it sends the message to nodes with subscribers of that topic, and then delivers the message to all subscribing actors on that node. The message is sent over the wire only once per node that has at least one subscriber of the topic. The decoupling of publisher and subscriber makes it easy to add and remove nodes in the cluster as needed.
Changes of subscribers are disseminated in a scalable way to other nodes with a gossip protocol. The registry is eventually consistent, i.e. changes are not immediately visible at other nodes, but typically they will be fully replicated to all other nodes after a few seconds.
At this stage of the tutorial, we've seen:
And now for something completely different.
So far you are running the application in one single JVM, hosting both frontend and backend. Let's try to add more backend and frontend nodes to the cluster.
Open a terminal and change directory to the root directory of the reactive-maps application. Start a backend node with the following command (on one line):
<path to activator dir>/activator
-Dakka.remote.netty.tcp.port=0
-Dakka.cluster.seed-nodes.1="akka.tcp://application@<IP_ADDRESS>:2552"
-Dakka.cluster.roles.1=backend-region
"run-main backend.Main"
This runs the backend.Main class and overrides the configuration to bind Akka remoting to a random available port and use the "backend-region" cluster role for this node. It also declares the node that is running as a seed node so that this node can find the already established cluster.
If you take a look at the log in Run you can see that the new node joined the cluster. The new node knows how to join the cluster because the first node running on port 2552 is configured as initial contact point in the 'seed-nodes' property in the application.conf. You can read more about Akka Clustering in the documentation.
You can repeat the command in new terminal windows to add more backend nodes.
You can also add more simulated users with the following command (on one line):
<path to activator dir>/activator
-Dakka.remote.netty.tcp.port=0
-Dakka.cluster.seed-nodes.1="akka.tcp://application@<IP_ADDRESS>:2552"
-Dakka.cluster.roles.1=frontend
-DreactiveMaps.bots.totalNumberOfBots=500
"run-main backend.Main"
The following command (on one line) will start another frontend node listening on HTTP port 9001:
<path to activator dir>/activator
-Dhttp.port=9001
-Dakka.remote.netty.tcp.port=0
-Dakka.cluster.seed-nodes.1="akka.tcp://application@<IP_ADDRESS>:2552"
-Dakka.cluster.roles.1=frontend
run
Try the added frontend in a new browser window: http://localhost:9001
Now that we've had a detailed look at some of the system, let's try and add a new feature. Until now, our view of the data has been region based - all data is associated with and stored in an actor for a region. This allows us to shard regions over multiple nodes, allowing efficient access to the data by node.
We're going to add functionality that is user based. We'll use exactly the same methods for scaling as for the region based data, so we can see how to build such a system from scratch.
We'll start off with a simple implementation that only works when there is one node, implementing first the backend, then the client side. Then we'll demonstrate how this implementation can be scaled out to shard the data across many nodes. Finally we'll show some techniques for ensuring data consistency when nodes are introduced into or removed from the cluster.
The new feature that we'll add is tracking the distance that a user has travelled. We'll make the client fetch this data when a user clicks on a marker.
The first thing we need to do is add a function for calculating the distance between two points. There are many such formulas that can be used to do this, but a simple general purpose one which will suit our purposes is called the haversine formula.
Let's create a new method called distanceBetweenTwoPoints
in
GeoFunctions
:
def distanceBetweenPoints(pointA: LatLng, pointB: LatLng): Double = {
import Math._
// Setup the inputs to the formula
val R = 6371009d // average radius of the earth in metres
val dLat = toRadians(pointB.lat - pointA.lat)
val dLng = toRadians(pointB.lng - pointA.lng)
val latA = toRadians(pointA.lat)
val latB = toRadians(pointB.lat)
// The actual haversine formula. a and c are well known value names in the formula.
val a = sin(dLat / 2) * sin(dLat / 2) +
sin(dLng / 2) * sin(dLng / 2) * cos(latA) * cos(latB)
val c = 2 * atan2(sqrt(a), sqrt(1 - a))
val distance = R * c
distance
}
The first thing we need to implement is a user meta data register. As a first implementation, we'll write one
actor that will store all user meta data, we'll call it UserMetaData
. Create a new file called
app/backend/UserMetaData.scala
now, and start off by adding the following:
package backend
import akka.actor.{Props, Actor}
import play.extras.geojson.LatLng
object UserMetaData {
case class GetUser(id: String)
case class User(id: String, distance: Double)
case class UpdateUserPosition(id: String, position: LatLng)
val props = Props[UserMetaData]
}
Some of the imports are unused for now, but the important thing to see here is the message types we've defined.
This actor will receive UpdateUserPosition
messages to update the user position, and will receive
GetUser
messages, and send back User
messages to the sender.
Now implement the actor itself:
class UserMetaData extends Actor {
import UserMetaData._
val settings = Settings(context.system)
var users = Map.empty[String, (LatLng, Double)]
def receive = {
}
}
Our actor depends on the settings (which provides the GeoFunctions
class we already edited), and
has a map that maps the userId to a tuple of the last position the user was seen at, and the distance they've
travelled. We'll now implement handling the GetUser
message in the receive
method:
case GetUser(id) =>
users.get(id) match {
case Some((_, distance)) => sender ! User(id, distance)
case None => sender ! User(id, 0)
}
You can see that if the user wasn't found, we just return 0. Now implement handling the
UpdateUserPosition
message:
case UpdateUserPosition(id, position) =>
val distance = users.get(id) match {
case Some((lastPosition, lastDistance)) =>
lastDistance + settings.GeoFunctions.distanceBetweenPoints(lastPosition, position)
case None => 0
}
users += (id -> (position, distance))
You can see here we're using the distanceBetweenPoints
method we implemented earlier, updating the
distance if we have a last position to compare it to, and updating the map with the new user data.
Now we need to implement the code that sends the user position updates to this actor. This is a little tedious because there a two things that will do this, the web client, and the bots. We'll start with the web client.
The actors for the web client are initialized by a Play module called Actors
. It's using some
Play helpers for creating and injecting actors, described
here. We want to define a new actor binding for the userMetaData
:
bindActor[UserMetaData]("userMetaData")
The class that will ultimately use this actor is the
ClientConnection.scala
actor. Open it,
and modify the constructor of ClientConnection
to accept it as a parameter:
class ClientConnection @Inject() (@Named("regionManagerClient") regionManagerClient: ActorRef,
@Named("userMetaData") userMetaData: ActorRef,
@Assisted email: String, @Assisted upstream: ActorRef) extends Actor {
Now when the client sends a UserMoved
message through the web socket, in addition to sending a
message to the regionManagerClient
, we also want to update the user meta data:
import backend.UserMetaData.UpdateUserPosition
userMetaData ! UpdateUserPosition(email, point.coordinates)
The web front end is set to go, but the bots also need to be updated. As with ClientConnection
,
add a constructor parameter for the user meta data to GeoJsonBot.scala
:
class GeoJsonBot(trail: LineString[LatLng], offset: (Double, Double), userId: String,
regionManagerClient: ActorRef, userMetaData: ActorRef) extends Actor {
And immediately after the bot sends a position update to the regionManagerClient
, make it also
send one to userMetaData
:
import backend.UserMetaData.UpdateUserPosition
userMetaData ! UpdateUserPosition(userId, userPos.position)
Update the props method:
def props(trail: LineString[LatLng], offset: (Double, Double), userId: String, regionManagerClient: ActorRef,
userMetaData: ActorRef): Props =
Props(classOf[GeoJsonBot], trail, offset, userId, regionManagerClient, userMetaData)
Now this is called by BotManager
, modify the props method, constructor parameter and the
the call that creates the bot:
def props(regionManagerClient: ActorRef, userMetaData: ActorRef, data: Seq[URL]): Props =
Props(classOf[BotManager], regionManagerClient, userMetaData, data)
class BotManager(regionManagerClient: ActorRef, userMetaData: ActorRef, data: Seq[URL]) extends Actor {
context.actorOf(GeoJsonBot.props(route, offset, userId, regionManagerClient, userMetaData))
BotManager
is initialised in two places, once in the web front end by Play in the
BackendActors
class, so update that to pass the userMetaData
:
class BackendActors @Inject() (system: ActorSystem, configuration: Configuration, environment: Environment,
@Named("regionManagerClient") regionManagerClient: ActorRef,
@Named("userMetaData") userMetaData: ActorRef) {
system.actorOf(BotManager.props(regionManagerClient, userMetaData, findUrls(1)))
And finally, we need to update the Main
class, this
is used when running a non Play node in the cluster. Initialise the UserMetaData
actor after
creating RegionManagerClient
:
val userMetaData = system.actorOf(UserMetaData.props, "userMetaData")
Now pass the actor to the BotsManager
props methed:
system.actorOf(BotManager.props(regionManagerClient, userMetaData, findUrls(1)))
The users distance is going to be requested by the user on an as needed basis. Since the action is triggered by the user and the user expects a response, it makes sense in this case to use a simple HTTP request to get the data. So we're going to write a Play action to get the user meta data.
Create a new Scala class called controllers.UserController
:
package controllers
import akka.actor.ActorRef
import akka.pattern.{AskTimeoutException, ask}
import akka.util.Timeout
import play.api.mvc._
import play.api.libs.json.Json
import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
import javax.inject.{Inject, Named}
import backend.UserMetaData._
class UserController @Inject() (
@Named("userMetaData") userMetaData: ActorRef
)(implicit ec: ExecutionContext) extends Controller {
}
There are quite a number of imports here, but don't be put off! The first thing we will do is define the
return format for the user meta data. We're going to return JSON, so we need something to convert instances of
User
into JSON. We can do this by implementing a JSON Writes
for User
.
Play's JSON API provides a handy macro that does this for you at compile time, so you can have type safe JSON
converters with a minimum of code. Create this writes instance in the UserController
object:
implicit val userWrites = Json.writes[User]
Since it is declared to be implicit, it will be used whenever we call a method that requires an implicit writes
for User
, which the Json.toJson
method does in the next block of code that we'll
write:
def get(id: String) = Action.async {
implicit val timeout = Timeout(2.seconds)
(userMetaData ? GetUser(id))
.mapTo[User]
.map { user =>
Ok(Json.toJson(user))
} recover {
case _: AskTimeoutException => NotFound
}
}
Here we have declared the action itself. It's an asynchronous action, meaning it returns a future. The action also takes a parameter, the id of the user. We'll see later how that parameter is passed to the action.
As a first step we have defined a timeout. In the code below that, we use the ask pattern, represented by the
?
operator, to ask the user meta data action for the user. Akka will not let you ask for something
without specifying a timeout, the ?
parameter takes an implicit timeout, so we've defined that to
be two seconds.
Having asked the actor for a user, we get back a future, and the first thing we do is map it to the type of
User
. Then we map that to something that will generate our response, we're returning an
Ok
response, with the body being the user serialised to JSON.
Finally, we also want to recover
from a timeout. We assume that if it timed out, it means the
user could not be found.
The last thing we need to do on the server side is declare how requests will be routed to this action. We do
this in the routes
file:
GET /user/:email controllers.UserController.get(email)
You can see that we are defining a dynamic route with an email
parameter, signalled by the colon
before the parameter name. Then we invoke the action we just created, passing that email
parameter as the id of the user.
Now that we've got the server side ready, we can write the client side code to consume the new action we just
created. Let's start with creating a new module,
app/assets/javascripts/services/userInfo.coffee
, that will
make the AJAX call. Although this module will be very simple, it's best practice to split out the making of
AJAX calls from the business logic of your code, so you can easily mock and test.
define ["jquery"], ->
{
get: (email) ->
$.getJSON("/user/" + email)
}
Our userInfo
module depends on jquery
, and simply provides one method,
get
, which calls the action. jQueries getJSON
method returns a promise of the json,
so we can consume that by attaching then
or done
callbacks to the returned promise.
Now in marker.coffee
, we want to
use this service to look up the user info. So we will add the userInfo
module as a dependency. If
you're familiar with requireJS, you might notice that we're not using a relative path name here, this is because
we are using path aliases that we configure elsewhere, this also makes mocking dependencies simpler, as it
decouples modules from their implementation.
define ["leaflet", "markerRenderer", "userInfo"], (Leaflet, renderer, userInfo) ->
In the constructor of the Marker
class, after attaching the popup to the marker, we want to bind
to the click event of the marker so that we can update the popup with the users distance each time the user
clicks on it:
@marker.bindPopup(renderer.renderPopup(userId))
@marker.on "click", =>
userInfo.get(userId).done (user) =>
@marker.getPopup()
.setContent(renderer.renderPopup(userId, user.distance)).update()
And now we want to handle that additional distance parameter that we've passed to renderPopup
,
in markerRenderer.coffee
:
renderPopup: (userId, distance) ->
popup = "<p><img src='http://www.gravatar.com/avatar/" +
md5(userId.toLowerCase()) + "'/></p><p>" + escapeHtml(userId) + "</p>"
if (distance)
popup + "<p>Travelled: " + Math.floor(distance) + "m</p>"
else
popup
Finally, since we've defined the new userInfo
module, we need to declare what the path for that
module is. This can be done in main.coffee
:
userInfo: "./services/userInfo"
And now we should be good to go, refresh your browser, and try clicking on a marker to see if the distance is rendered. If you're looking at the bots walking around North Carolina, you might see that they are moving at hundreds of metres per second - these bots are designed to provide interesting data, not necessarily realistic data.
After making the previous changes to the client side logic, we've left some of the client side tests in a failing state. Let's fix them. Start by running them to see that they are failing, by going to the test tab. Run the tests, you should see some of them are failing.
We're using mocha to write tests, in combination with Squire.js to mock out Require.js dependencies. We've also been very careful to design our client side code in such a way that the DOM manipulation code, and any code doing AJAX or WebSockets or using any other browser based APIs are separated from the business logic. This allows us to comprehensively test the important code.
Open MarkerSpec.coffee. This is where our failing tests are. Since we've added a new user info service that Marker.coffee depends on, we need to create a mocked version for this:
class MockUserInfo
users: {}
get: (userId) ->
new MockPromise({
distance: @users[userId]
})
Now to test that the marker correctly passes the looked up user distance to marker renderer, we'll modify the
MockMarkerRenderer.renderPopup
method to "render" it:
renderPopup: (userId, distance) ->
if distance
userId + ":" + distance
else
"Popup " + userId
Now lets modify the test setup code to instantiate and mock the mock user info service that we created:
# Create mocks
leaflet = new MockLeaflet()
renderer = new MockMarkerRenderer()
userInfo = new MockUserInfo()
# Mockout require js environment
new Squire()
.mock("markerRenderer", renderer)
.mock("leaflet", leaflet)
.mock("userInfo", userInfo)
.require ["javascripts/map/marker"], (Marker) ->
test({
leaflet: leaflet,
userInfo: userInfo,
renderer: renderer,
}, Marker)
done()
Now try running the tests again. They should pass. Finally, let's add a new test that tests that the popup is updated with the distance when the marker is clicked:
it "should update the popup with the current distance when clicked", testMarker (deps, Marker) ->
marker = new Marker(new MockMap(), single, new LatLng(10, 20))
deps.userInfo.users["userid"] = 50
marker.marker.onClick()
assert.equal("userid:50", marker.marker.popup.content)
Run the tests to ensure the new test also passes.
So now we've implemented something that works on a single node. However, this application has been designed to scale to millions of users on hundreds of nodes - a feature that only works on a single node will not suffice.
The system should also be resilient to system crashes and restarts. To address these issues we will make it persistent and distribute the actors over the backend nodes in the cluster.
The UserMetaData actor that we developed previously holds the data for all users, which is obviously not scalable. Instead, let's rewrite the actor to represent the data for one single user. We will use Akka Persistence to make it durable.
package backend
import java.net.URLDecoder
import scala.concurrent.duration._
import akka.actor.PoisonPill
import akka.actor.Props
import akka.actor.ReceiveTimeout
import akka.contrib.pattern.ShardRegion
import akka.persistence.EventsourcedProcessor
import play.extras.geojson.LatLng
object UserMetaData {
case class GetUser(id: String)
case class User(id: String, distance: Double)
case class UpdateUserPosition(id: String, position: LatLng)
val props = Props[UserMetaData]
sealed trait Event
case class FirstObservation(position: LatLng) extends Event
case class Moved(to: LatLng, distance: Double) extends Event
private case class State(position: Option[LatLng], distance: Double) {
def updated(evt: Event): State = evt match {
case Moved(to, d) => copy(position = Some(to), distance = distance + d)
case FirstObservation(pos) => copy(position = Some(pos))
}
}
}
class UserMetaData extends EventsourcedProcessor {
import UserMetaData._
val settings = Settings(context.system)
val userId = URLDecoder.decode(self.path.name, "utf-8")
private var state = State(None, 0.0)
// passivate the entity when no activity
context.setReceiveTimeout(30.seconds)
override def receiveRecover: Receive = {
case evt: Event => state = state.updated(evt)
}
override def receiveCommand: Receive = {
case _: GetUser =>
sender() ! User(userId, state.distance)
case UpdateUserPosition(_, position) =>
state match {
case State(Some(lastPosition), _) =>
val d = settings.GeoFunctions.distanceBetweenPoints(lastPosition, position)
persist(Moved(position, d)) { evt =>
state = state.updated(evt)
}
case State(None, _) =>
persist(FirstObservation(position)) { evt =>
state = state.updated(evt)
}
}
case ReceiveTimeout =>
context.parent ! ShardRegion.Passivate(stopMessage = PoisonPill)
}
}
Akka Persistence takes an event sourced approach and stores the changes that build up its current state.
In this case the current position and the total distance is stored by the events
FirstObservation
and Moved
.
It is recommended to encapsulate the state in an immutable class as illustrated
in the UserMetaData.State
class. It knows how to create a new State
instance when applying the changes represented by domain events. It is important that the
state updates are free from side effect, because they are applied when the actor
is recovered from the persisted events. See receiveRecover
.
Akka cluster Sharding is useful when you need to distribute actors across several nodes in the cluster and want to be able to interact with them using their logical identifier, but without having to care about their physical location in the cluster, which might also change over time.
To use the UserMetaData
actor with cluster sharding we must be able to
extract the identifier from the messages and define a hash function for the identifier.
The hash function is used to group actors into shards, potentially running on different
nodes.
Those functions can be defined in the companion object like this:
sealed trait Command {
def id: String
}
case class GetUser(id: String) extends Command
case class User(id: String, distance: Double) extends Command
case class UpdateUserPosition(id: String, position: LatLng) extends Command
val idExtractor: ShardRegion.IdExtractor = {
case cmd: Command => (cmd.id, cmd)
}
val shardResolver: ShardRegion.ShardResolver = msg => msg match {
case cmd: Command => (math.abs(cmd.id.hashCode) % 100).toString
}
val shardName: String = "UserMetaData"
To make the UserMetaData
actors sharded in the cluster we need to register it to the
ClusterSharding
extension. This must be done on all nodes in the cluster,
but we do it slightly different on the nodes with frontend
role compared to
backend
because the actors must only be created on the backend nodes, and only
proxied from frontend nodes. Add the following in Actors.scala
import akka.contrib.pattern.ClusterSharding
if (Cluster(system).selfRoles.exists(r => r.startsWith("backend"))) {
system.actorOf(RegionManager.props(), "regionManager")
ClusterSharding(system).start(
typeName = UserMetaData.shardName,
entryProps = Some(UserMetaData.props),
idExtractor = UserMetaData.idExtractor,
shardResolver = UserMetaData.shardResolver)
} else {
ClusterSharding(system).start(
typeName = UserMetaData.shardName,
entryProps = None,
idExtractor = UserMetaData.idExtractor,
shardResolver = UserMetaData.shardResolver)
}
Our setup for the user meta data actor is now a little more complex than the Play bindings helper will allow, so
we need to create a new provider for it, and bind it to be an eager singleton. This can be done using the
javax.inject.Provider
interface:
import com.google.inject.name.Names
bind(classOf[ActorRef]).annotatedWith(Names.named("userMetaData"))
.toProvider(classOf[UserMetaDataProvider]).asEagerSingleton()
class UserMetaDataProvider @Inject() (system: ActorSystem) extends Provider[ActorRef] {
lazy val get = ClusterSharding(system).shardRegion(UserMetaData.shardName)
}
And corresponding in Main.scala:
import akka.contrib.pattern.ClusterSharding
ClusterSharding(system).start(
typeName = UserMetaData.shardName,
entryProps = Some(UserMetaData.props),
idExtractor = UserMetaData.idExtractor,
shardResolver = UserMetaData.shardResolver)
ClusterSharding(system).start(
typeName = UserMetaData.shardName,
entryProps = None,
idExtractor = UserMetaData.idExtractor,
shardResolver = UserMetaData.shardResolver)
val userMetaData = ClusterSharding(system).shardRegion(UserMetaData.shardName)
When using Akka Persistence in a cluster we need a journal that is replicated or accessible from all nodes. In this sample we will use a shared LevelDB journal running on the node with port 2551. This is a single point of failure, and should not be used in production. A real system would use a distributed journal.
Add the configuration for the journal:
persistence {
journal.plugin = "akka.persistence.journal.leveldb-shared"
journal.leveldb-shared.store {
# DO NOT USE 'native = off' IN PRODUCTION !!!
native = off
dir = "target/shared-journal"
}
snapshot-store.local.dir = "target/snapshots"
}
Add the file app/backend/SharedJournalHelper.scala with the following content:
package backend
import scala.concurrent.duration._
import akka.actor.ActorIdentity
import akka.actor.ActorPath
import akka.actor.ActorSystem
import akka.actor.Identify
import akka.actor.Props
import akka.pattern.ask
import akka.persistence.journal.leveldb.SharedLeveldbJournal
import akka.persistence.journal.leveldb.SharedLeveldbStore
import akka.util.Timeout
import akka.cluster.Cluster
object SharedJournalHelper {
def startupSharedJournal(system: ActorSystem): Unit = {
// Start the shared journal one one node (don't crash this SPOF)
// This will not be needed with a distributed journal
val storePort = 2552
if (Cluster(system).selfAddress.port.get == storePort)
system.actorOf(Props[SharedLeveldbStore], "store")
// register the shared journal
import system.dispatcher
implicit val timeout = Timeout(10.seconds)
val storePath = ActorPath.fromString(s"akka.tcp://${system.name}@<IP_ADDRESS>:$storePort/user/store")
val f = (system.actorSelection(storePath) ? Identify(None))
f.onSuccess {
case ActorIdentity(_, Some(ref)) => SharedLeveldbJournal.setStore(ref, system)
case _ =>
system.log.error("Shared journal not started at {}", storePath)
system.shutdown()
}
f.onFailure {
case _ =>
system.log.error("Lookup of shared journal at {} timed out", storePath)
system.shutdown()
}
}
}
Initialize the shared journal in Actors.scala
// This will not be needed with a distributed journal
SharedJournalHelper.startupSharedJournal(system)
And corresponding in Main.scala:
// This will not be needed with a distributed journal
SharedJournalHelper.startupSharedJournal(system)
We have now added scalability and resilience to the new user meta data feature. Try it again, by refreshing your browser at http://localhost:9000.
The files of the shared journal are saved in the target directory and when you restart the application the state is recovered. You can clean the state with:
<path to activator dir>/activator clean
We have developed ConductR; a solution that is designed to ease the deployment and management of reactive applications and services. ConductR handles failures gracefully, scales elastically and embraces change in your Reactive system. Enough of the sales talk though, let's ship this app!
For any application that will target ConductR you first need to add the sbt plugin sbt-conductr to your project/plugins.sbt. You will also need to have installed Docker and the conductr-cli in order to run a local ConductR cluster within the sandbox.
sbt-conductr actually brings in another sbt auto plugin named BundlePlugin and between them, you declare some settings required for deploying your application. You then load and run a "bundle" which is the unit of deployment for ConductR. A bundle is a zip file with the hash of its contents encoded in its name.
The sandbox allows you to deploy your application on a local ConductR cluster for the purposes of testing. The sandbox provides a visualization of the loaded and running bundles, application log reporting within the comfort of sbt and proxying of your services. You can therefore get your application perfectly prepared for deploying into more production-like environments that run ConductR (such as EC2).
In terms of modifying ReactiveMaps very little is required. The essential requirement is that your application must signal ConductR that is it ready upon having started up. Note the lines that obtain a BUNDLE_SYSTEM and BUNDLE_SYSTEM_VERSION. These environment variables are set in the context of ConductR. In our case we use these information to specify the actor system name. The latter lines signal to ConductR that the app has started up. Also note that this code is only required when running pure Akka applications such as the backend of ReactiveMaps. For Play, no configuration or code change is necessary. Both the Akka and Play approaches utilize a library named conductr-bundle-lib. That's it!
The following describes something that can only be done from the activator command line. You'll need to jump into a separate terminal window and just type "activator" to fire up the activator console.
To prepare ConductR's bundles, with activator type:
bundle:dist
backend-region:dist
backend-summary:dist
Three separate bundles will then be generated. Note the files produced.
We then start up the sandbox so that we can load and then run the bundles we've produced:
sandbox run
The sandbox will report the address that bundles will be found on. It will be something like <IP_ADDRESS>. Take note of this address.
You can then load a bundle and run it using the following commands (substitute your paths and hashes as appropriate):
conduct load /pathtotheproject/target/bundle/reactive-maps-v1-76d4af00c95dbed5eddc4a7a93a753bf7a85c14771745eea79239b52f61b90a0
conduct run reactive-maps-frontend
Do the above for each bundle that you produced.
Once your bundles are loaded and running then you can type "conduct info" in order to see the state of the world. You should see something like this:
ID NAME #REP #STR #RUN
76d4af0-03202dc reactive-maps-frontend 1 0 1
76d4af0-088d9a0 reactive-maps-backend-region 1 0 1
76d4af0-52e1a1d reactive-maps-backend-summary 1 0 1
You can then visit your application by navigating to the address that the sandbox provided, and the port that is configured for ReactiveMaps e.g. http://<IP_ADDRESS>:9000. The ip address is your corresponding docker host ip address.
With a production cluster (not the sandbox, which is only single noded!), you'd now be able to scale up instances of each of these bundles by simply typing "conduct run reactive-maps-frontend --scale 3" to get 3 instances of the frontend. Note that there is no need to specify the seed nodes manually. ConductR takes care of that for you. ConductR will also keep ReactiveMaps running should a node fail, or if itself fails. Resiliency is at the core of ConductR.
Essential characteristic of a reactive application:
Read more about how to build reactive applications in the Reactive Manifesto.
Find out more about the Typesafe Reactive Platform.


Posted 31 May, 2017- About 6 mins to read
So everyone is talking about this hip “new” kid on the block for PHP: Generics. The RFC is on the table and a lot of people are getting all excited about it, but you don’t fully see the excitement? Let’s explore what it’s all about!
What is the use case of a Generic?
Generics is not something new. Statically typed languages like Java and C# have had them for ages. There are even some dynamically typed languages like Dart that implement them in their core. And even languages that don’t support Generics like for example Javascript can still support them with a 3rd party implementation, in the case of Javascript this would be Typescript.
The basic idea is that you can reuse a class that is statically typed with different typed parameters. OK, I understand that this might sound very confusing. So let’s check out an example:
|
|
As you can see, we are creating a Car
object with 2 parameters. The wheels and the engine.
Now this is all fine and dandy, but what do $wheels
and $engine
parameters represent? In this case it’s the amount of wheels and what engine it sports one would assume, as we send an integer and a string along.
But $car = new Car(‘pirelli’, true);
would be just as valid.
This is of course one of the typical things of a dynamically typed language. But if we want to add some more uniformity in our class we could, thanks to PHP 7 and types:
|
|
Now it’s impossible to create an invalid object thanks to the strict types. OK cool, let’s make everything strictly typed then!
But what if we want to create an object that’s both statically and dynamically typed? This sounds absurd but let me give you a use case. You want an object that accepts a parameter on creation but it needs to always return that same type of parameter on its return.
So for example when I do $myCar = new Car('honda');
and later $myCar->getEngine();
I want to be sure that what I get back from getEngine
is the same type as what I’ve put into the constructor (in this case a string).
OK show me this generic magic then!
Well… the thing is… at the time of writing, generics don’t yet exist in PHP yet. I know… what a tease. But luckily for us there is an RFC! So people are still loudly arguing whether or not they want to include generics into PHP and how it should look. But let us take a look at what the current proposed way of doing it is.
The use case described above could look like this:
|
|
|
|
Here we say, we are going to create a new Car, with 2 parameters; an integer and a string. You could according to the currently proposed RFC just send:
|
|
but I like the explicit version better for explanation sake. Next up is:
|
|
This tells the class about what variable types it supports.
And then we can use these virtual types just like normal types in the class.
So in this case the WheelType
will be an integer and the EngineType
a string.
It’s as easy as that.
That’s a use case that will never happen!
It is indeed a niche use case, but as PHP grows towards a hybrid of a dynamically typed and strictly typed language it does make sense to include some more features of strictly typed languages. While true, you will not use this everyday, there is no harm in having it in the language. It would however be handy for people that write logging or collection packages. Take this piece of code for example:
|
|
You can now reuse this collection for whatever you want, but you will always be sure getValues()
will return a collection of items that have the same type.
Even more fun is that you can instantiate a Collection like this: $collection = new Collection<Runnable >();
where Runnable
is an interface. This way you could have, for example, a collection that you are sure of only has items with values objects in them that conform to the interface. For example:
|
|
The <T is Runnable>
in this context would stand for: “you can enter whatever type you want here, but it has to adhere to the Runnable
interface.”
So what’s next? As I said before, the proposal is currently in draft. Once the proposal is finished it will go into a voting round and if there is a consensus about implementing it, it will be added, otherwise it’s back to the drawing board for Generics in PHP.
p.s. you can follow along how the RFC is going here


{% extends "layout.html" %} {% block page_title %} Food {% endblock %} {% block content %} {% include "includes/phase_banner_beta.html" %} {{formData | safe}} Have all these people already had a criminal check? Have all these people already a had check? These criminal checks are called DBS check (previously CRB checks) Yes No Not sure {% endblock %}
These criminal checks are called DBS check (previously CRB checks)


{{partial "header"}}
The API for the SNOMED Release Service is in production, however the user interface will be developed at a later date.


This 1-Dimensional Elementary Cellular Automata (1D ECA) is a Cellular Automata (CA) where the current state is only one dimension and where each cell is either black or white. The next state is determined by the state of the cell above and its neighbors as dictated by the rules. The neighborhood usually includes the adjacent cells but could include other faraway cells. There are numerous sets of rules that dictate the next state. The example here models one set of rules. How many rule variations are possible?
To engage the simulator:
To explore:


3303 S 12th Ave | Tucson, Az 85713 |<PHONE_NUMBER>
Tues-Sat 7am-8pm Sun 8am-2pm
Toggle navigation
3303 S 12th Ave
Tucson, Az 85713
(520) 203-7569
Home
Menu
Catering
Contact
Café Santa Rosa
Menu
Breakfast
(served until 11:00 AM)
Breakfast Burros
B1 - Chorizo & Egg - $6.25
B2 - Chorizo, Egg & Potato - $6.75
B3 - Egg, Potato & Bacon or Sausage - $6.95
B4 - Egg, Potato & Beans - $5.50
B5 - Egg & Potato - $5.00
B6 - Corned Beef & Potato - $6.25
B7 - Red Chili & Egg - $7.75
All Breakfast Burros add Cheese or Beans or 1 egg for $0.50
Combination Plates
All combination plates include 1 popover or 1 tortilla
BC1 - Two Eggs, Chorizo, Beans & Potatoes - $8.50
BC2 - Two Eggs, Bacon or Sausage, Beans & Potatoes - $8.95
BC3 - Two Eggs, Beans & Potatoes - $6.50
BC4 - Two Eggs, Red or Green Chili & Beans - $9.50
BC5 - Corned Beef & Potatoes with 2 Eggs & Beans - $8.50
Sides
Egg - $1.00
Bacon or Sausage - $3.50
White Menudo
Served Sundays Only
Small Bowl - $6.50
Large Bowl - $7.25
Lunch and Dinner
(Served All Day)
Burros
Red or Green Chili - $7.25
Red or Green Chili & Beans - $7.75
Taco Burro - $7.50
Bean - $5.95
Squash & Cheese - $7.00
Popovers
(Indian Fry Bread)
Red or Green Chili - $7.75
Red or Green Chili & Beans - $7.95
Bean - $6.25 (Add cheese for $.50)
Indian Taco (Red Chili) - $9.25
Indian Taco (ground Beef, Beans, Lettuce, tomato & Cheese) - $8.75
Indian Burger(2 hamburger patties, green chili, cheese, lettuce, tomato and onion) - $10.25
Honey or Powder Sugar - $4.50
Honey & Powder Sugar - $4.75
Chocolate & Powder Sugar - $4.75
Combination Plates
(Comes with a choice of Rice,
Squash or Potato Salad)
C1 - Ground Beef Tacos with Beans - $9.75
C2 - Potato & Ground Beef tacos with Beans - $9.99
C3 - Red or Green Chili & Beans with 1 tortilla or 1 Popover - $10.25
C4 - Two Chorizo & Bean Tostadas - $8.50
C5 - Two Flat Enchiladas with Beans - $9.99
C6 - Cheese Quesadilla with Beans - $6.50
Side Orders
Beans - $2.95
Rice - $2.95
Squash - $3.50
Tortilla - $1.75
Popover - $2.75
Potato Salad - $3.00
Ground Beef - $3.00
Red or Green Chile - $4.75
Honey - $1.75
Two Stripes of Roasted Green Chile - $1.75
Drinks
Soda Pop - $2.25
Tea - $2.25
Coffe - $2.25
Orange Juice - $2.50


Themextractor
The best way to not read a text and know what it's about
Put your text here:
Number of results
1
2
3
4
5
6
7
Go!
#
Topic
Hits
1
Politics
30%
2
Geography
27.3%
3
Law
15.8%
Chart


Please click on and proceed by selecting a project from the list.
You may also continue labeling where you have left Cytomine IRIS last time. In order to jump right to the annotation you viewed last, click on the image name that is displayed at the top of the page.
Alternatively you may also press the key on your keyboard.Open the list of keyboard shortcuts available for this page by pressing on your keyboard.


SWINDLE, THE (RIEN NE VA PLUS)(director/writer: Claude Chabrol; cinematographer: Eduardo Serra; editor: Monique Fardoulis; cast: Isabelle Huppert (Betty), Michel Serrault (Victor), Francois Cluzet (Maurice), Jean-Francois Balmer (Monsieur K), Jackie Berroyer (Chatillon), 1997-France)
Reviewed by Dennis Schwartz
This is one of Chabrol's poorer films, the fiftieth one for the 68-year-old French director. It has the look and script of a B-film (even though it was written expressly for the two stars). Chabrol is playful here, but his lifetime of running gags against the bourgeoisie and his attempt to make this a giddy film, just didn't hold up. Though, there are some delightfully coy moments, as a pair of small time scam artists, the wily, 70ish Victor (Michel Serrault) and the 40ish, sex pot, Betty (Isabelle Huppert), work their nonviolent schemes in a casino and at a dentist's convention. Their relationship is one of longtime friends, business partners, and possibly, at one time, they were lovers. But it was never clearly spelled out what their intimate relationship was, as Chabrol likes to keep one guessing. Betty, even sardonically, calls Victor daddy throughout much of the film. It wouldn't surprise me if they were related that way. The heart of the film, even more than their con games, becomes this precarious relationship between the older man and much younger woman, as it becomes anyone's guess what their real relation is and how they really feel about each other, and whether they are capable of swindling each other.
Betty picks up a fellow gambler at the casino's baccarat table and takes him to a bar, where she tells him she is a director of an insurance company and then slips a sleep inducing narcotic into his drink and takes the romantically inclined lawn mower businessman back to his room in the hotel.
They next head separately for a Swiss resort Sils Maria (where Nietzsche wrote Thus Spake Zarathustra), near St. Moritz, to work one of their schemes on a dentists' convention. But when he arrives a bit later, using the pseudonym Colonel Emmanuel Victor, retired, a role which he plays with proper panache, and with Betty, now impersonating a Russian blonde named Sissi, who has swooped down onto a dashing young businessman courier, Maurice (Francois Cluzet). This relationship makes Victor jealous. But she tells him that he's their pigeon, he is carrying 5 million in Swiss francs in his attache case and is up to cheating his firm out of it. Victor is upset that she has switched the original plan he had for working a scam on the dentists, thinking this one is too big for them and too dangerous.
Maurice is scheduled to bring the money to Guadeloupe to be exchanged, and Chabrol adroitly uses all his many years of filmmaking skills to show double-crosses and switches of bags, and how things can go violently wrong with even the best of schemes. But, it is mostly a story about the two characters, their needs and how they relate to each other. Each character becoming unpredictable to the other one. That forms most of the wry comedy. There is also interest as to how and if the swindle can be pulled off by the couple, who, at times, have not told the other what they are up to.
This caper story seemed stale to me, even though it has a certain amount of charm. But it did not splash across the screen with verve and audacity as much as Chabrol intended it to. The director seemed to try too hard to relive his past with many allusions to characters and inside jokes from his many films, with the old master pulling out his tricks of the trade from a hat with too much ease to be believable.
REVIEWED ON 3/27/2000 GRADE: C-
Dennis Schwartz: "Ozus' World Movie Reviews"
http://www.sover.net/~ozus
<EMAIL_ADDRESS>© ALL RIGHTS RESERVED DENNIS SCHWARTZ
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


The purpose of this challenge is to demonstrate the MITRE Top 25 programming flaw: 'Download of Code Without Integrity Check'.
The product downloads source code or an executable from a remote location and executes the code without sufficiently verifying the origin and integrity of the code.
Security breaches are usually done by leveraging multiple vulnerabilities also known as "death by 1000 cuts".
In the previous challenge you broke into the admin's account using a password guessing attack.
Now you take the breach to the next level by taking control over the server. You have to download the malware from your C&C server: evil.bad
.
Conveniently, there are no integrity checks in place.


from smplxn import *
import numpy as np
◇
Macro referenced in 14b.
An useful utility will allow for the creation of a subdirectory from a dirpath string.
import os
def createDir(dirpath):
if not os.path.exists(dirpath):
os.makedirs(dirpath)
◇
It may be useful to define the repository(ies) for the unit tests associated to the module:


Enumerations
The following enumerations are available globally.
-
See moreRSDAsyncActionStatus
is an enum used to track the status of aRSDAsyncAction
.Declaration
Swift
@objc public enum RSDAsyncActionStatus : Int
-
General-purpose enum for authorization status.
See moreDeclaration
Swift
@objc public enum RSDAuthorizationStatus : Int
-
List of the rules to apply when navigating based on the currently applied cohorts.
See moreDeclaration
Swift
public enum RSDCohortRuleOperator : String, Codable
-
An enum for part of the view to which a given color style should be applied.
See moreDeclaration
Swift
public enum RSDColorPlacement : String, Codable
-
A listing of the color names that are already assigned by the base framework using
See moreColorMatrix.json
as the source file.Declaration
Swift
public enum RSDReservedColorName : RawRepresentable, Equatable, Hashable, Codable
-
A list of reserved filenames for data added to an archive that is keyed to a custom-built data file.
See moreDeclaration
Swift
public enum RSDReservedFilename : String
-
See moreRSDFormDataType
is used to describe the data type for a form input. This is different from theRSDAnswerResultType
which is a struct that can be used to encode and decode the input field answer value. This describes the type of information required by the input field.Declaration
Swift
public enum RSDFormDataType
-
The frequency type can be used to indicate the frequency with which to do something within the app.
See moreDeclaration
Swift
public enum RSDFrequencyType : String, CaseIterable
-
What source should be used for the image selection?
See moreNote
The app is required to include the appropriate permission strings in itsInfo.plist
file.Declaration
Swift
public enum RSDImagePickerSourceType : String, Codable
-
The capture mode sets whether to capture video or a photo.
Declaration
Swift
public enum RSDImagePickerMediaType : String, Codable
-
See moreRSDInputFieldError
is used by theRSDFormStepDataSource
when validating a user-entered answer for a given input field.Declaration
Swift
public enum RSDInputFieldError : Error
-
RSDMotionRecorderType
is used to enumerate the sensors and calculated measurements that can be recorded by theRSDMotionRecorder
.RSDMotionRecorder
records each sample from either the raw CoreMotion sensors (accelerometer, gyro, and magnetometer) or the calculated vectors returned when requestingCMDeviceMotion
data updates. TheCMDeviceMotion
data is split into the components enumerated by this enum into a single vector (sensor or calculated) per type.By default, the requested types are are saved to a single logging file as instances of
RSDMotionRecord
structs.Spliting the device motion into components in this manner stores the data in using a consistent JSON schema that can represent the sensor data returned by both iOS and Android devices. Thus, allowing research studies to target a broader audience. Additionally, this schema allows for a single table to be used to store the data which can then be filtered by type to perform calculations and DSP on the input sources.
See moreDeclaration
Swift
public enum RSDMotionRecorderType : String, Codable, RSDStringEnumSet
-
RSDDatePickerMode
describes the type of UI picker to display for dates and times.See moreSeealso
RSDDatePickerDataSource
Declaration
Swift
public enum RSDDatePickerMode : String
-
See moreRSDResourceTransformerError
is used to support throwing errors when attempting to transform a resource.Declaration
Swift
public enum RSDResourceTransformerError : Error, CustomNSError
-
Standard permission types.
See moreNote
This framework intentionally does not include any direct reference to Health Kit. First, including Health Kit in applications that do not use that SDK makes it confusing and difficult for researchers to set up the app. Second, the goal of this framework is to include a model that is platform-agnostic and can be used independently of the device. (syoung 11/1/7/2017)Declaration
Swift
public enum RSDStandardPermissionType : String, RSDPermissionType, Codable, CaseIterable
-
See moreRSDPermissionError
errors are thrown when a task, step, or async action does not have a permission that is required to run the action.Declaration
Swift
public enum RSDPermissionError : Error
-
List of rules creating the survey rule items.
See moreDeclaration
Swift
public enum RSDSurveyRuleOperator : String, Codable, RSDStringEnumSet
-
The direction of navigation for the steps.
See moreDeclaration
Swift
public enum RSDStepDirection : Int, Codable
-
The possible errors thrown when fetching a task.
See moreDeclaration
Swift
public enum RSDTaskFetchError : Error
-
Codable
enum for the auto-capitalization type for an input text field.- keywords: [
none
,words
,sentences
,allCharacters
]
Declaration
Swift
public enum RSDTextAutocapitalizationType : String, Codable, RSDStringEnumSet
- keywords: [
-
Codable
enum for the auto-capitalization type for an input text field.- keywords: [
default
,no
, yes"]
Declaration
Swift
public enum RSDTextAutocorrectionType : String, Codable, RSDStringEnumSet
- keywords: [
-
Codable
enum for the spell checking type for an input text field.- keywords: [
default
,no
, yes"]
Declaration
Swift
public enum RSDTextSpellCheckingType : String, Codable, RSDStringEnumSet
- keywords: [
-
Codable
enum for the spell checking type for an input text field.- keywords: [
default
,asciiCapable
,numbersAndPunctuation
,URL
,numberPad
,phonePad
,namePhonePad
,emailAddress
,decimalPad
,twitter
,webSearch
,asciiCapableNumberPad
]
Declaration
Swift
public enum RSDKeyboardType : String, Codable, RSDStringEnumSet
- keywords: [
-
The
See moreRSDUIActionType
enum describes standard navigation actions that are common to a given UI step. It is extendable using the custom field.Declaration
Swift
public enum RSDUIActionType
-
See moreRSDValidationError
errors are thrown during validation of a task, step, etc. Usually this happens during the decoding of the task or when the task is first started.Declaration
Swift
public enum RSDValidationError : Error
-
The weekday enum assigns an enum value to each day of the week and implements
See moreComparable
to allow for sorting the weekdays by the order appropriate for the participant’s current Locale.Declaration
Swift
public enum RSDWeekday : Int, Codable, RSDIntEnumSet


spark.repl.SparkMemberHandlers
The names imported by this statement
Whether this import includes a wildcard import
Complete list of names imported by a wildcard


Preparation time
30 mins to 1 hour
Cooking time
10 to 30 mins
Serves
Serves 4
Whole seabass baked in a salt crust served with a delicious potted shrimp, lemon and radish salad.
For the potted shrimp, place the butter into a saucepan then add the mace, nutmeg and cayenne pepper.
Heat over a gentle heat until the butter is melted then turn off the heat and set aside to infuse for an hour.
Strain the clarified butter through a fine sieve into a clean bowl then add the shrimp, shallot, lemon and dill.
Stir well to combine then season with salt and black pepper and place in the fridge to chill.
For the sea bass, preheat the oven to 220C/425F/Gas 7.
Place the sea bass onto a large baking tray.
Place the salt into a large bowl and gradually add the egg white, mixing well until you achieve the consistency of wet sand.
Pour the mixture over the sea bass and smooth to cover totally.
Place in the oven and bake for 20-25 minutes until the crust is golden brown and the bass is cooked – check by inserting a knife into the thick part of the flesh and ensuring that the fish has reached 38C/100F.
To serve, mix together the sliced fennel, radish and dill, season with lemon juice and olive oil, spoon in the potted shrimps and mix well.
Serve the salad on plates and the fish in the centre of the table.


Module: D2
- Extended by:
- CLI::Kit::Autocall
- Defined in:
- lib/d2/helpers/fzy.rb,
lib/d2.rb,
lib/d2/commands.rb,
lib/d2/commands/cd.rb,
lib/d2/entry_point.rb,
lib/d2/helpers/git.rb,
lib/d2/commands/help.rb,
lib/d2/commands/clone.rb,
lib/d2/commands/config.rb,
lib/d2/utils/first_run.rb,
lib/d2/utils/formatter.rb,
lib/d2/helpers/git/local_repo.rb
Overview
TODO: Test
Defined Under Namespace
Modules: Commands, Helpers, Utils Classes: Command, FileDescriptor
Constant Summary collapse
- TOOL_NAME =
The name of the tool
'd2'
- ROOT =
The root directory of the tool
File.('../..', __FILE__)
- LOG_FILE =
The file path where the log exists
'/tmp/d2.log'
- FILE_DESCRIPTOR =
The file descriptor object used to interact with the OS file descriptor 9 This is used by the Ruby process (child) to communicate with the parent process (shell). On finalization of the shell function, the file descriptor is used to manipulate the parent environment. Use D2::FILE_DESCRIPTOR.write(“cd blah”) to add to the file descriptor for evaluation in the parent.
FileDescriptor.new


Astrolog32 v.
1.20
Readme
Welcome to Astrolog32!
Astrolog32 is a free Windows
software with a rich feature set. It has most of the features
used by beginners, and some used by advanced students of
Astrology.
Astrolog was created by Walter Pullen, who finished
development in December 1998. Valentin Abramov continued
its development until April 2002. Astrolog32
is based on Astrolog version 5.41g for MS Windows, as modified
by Valentin. For a list of changes
done since 5.41g, please look at the bottom of file
"History A32.html".
Using
Astrolog32
for the first time
New users should do the
following:
- Unzip all files to c:\astrolog32, keeping the same folder
names as in the zip file. This will result in two sub-directories
being created, "main" and "interpretations". You may store
Astrolog32 in a different directory, but then on the next step you
will have to let Astrolog32 know where you put it.
- Double-click on "astrolog32.exe". You will be
asked if you want to configure Astrolog32. Say yes, and proceed
with configuration. First you should choose the regional settings
most appropriate to you. Next enter the coordinates and time zone
of where you live. Finally you can choose the directories used
by Astrolog32 . If you installed it in the default
directory, no change is needed at this time, otherwise
configure Astrolog32 with the directory where you stored
it.
- We recommend that you download the Swiss Ephemeris files
from the Astrolog32
site, or from AstroDienst.
With those files,
not only will you get more precise results, but calculations are
faster.
Operating
Systems
Supported
Tested
and it works:
Windows XP, Windows
2000, Windows NT 4.0,
Windows Millennium Edition, Windows
98.
Not tested, but expected to
work: MS Windows Server 2003.
Windows 95:
Most of the functionality will likely work, but some won't.
This software was not tested and is not recommended for
Windows 95.
Will not work: MS DOS,
MS Windows 3.1 or earlier, MAC OS, OS/2, Unix/Linux - for all these
operating systems, there are other Astrolog versions available that
work well.
Support
There are several
options:
- You can read files "helpfile.doc" or "helpfile.txt",
available in the distributed package. The contents are similar, the
difference is that one is a plain text file, while the other is in
Microsoft Word format. Both these files are very out of
date.
- There is an Help menu in Astrolog32.
- Help from other Astrolog users is available from Astrolog List.
You need to join the group to post a message.
Known Bugs
List
A list of bugs and suggestions
for new features can be found in the Astrolog32 Wish List,
available at Astrolog
List.
Disclaimer
This
software is provided "as is", without
warranty of any kind, either express or implied. In no event shall
the authors or copyright holders be liable for any claim, damages
or other liability, arising from the use or performance of this
software.


Please make it so that mm2 selects the servers (electrum/parity) it connects to by random out of the configured ones.
\"method\":\"electrum\",\"coin\":\"ETOMIC\",\"urls\":[\"electrum1.cipig.net:10025\",\"electrum2.cipig.net:10025\",\"electrum3.cipig.net:10025\"] should select one of the 3 servers by random, not simply connect to the first one.
atm, i see that most connections on the electrums are on electrum1 and pretty much none on electrum3.


Table of Contents - RDoc Documentation
Pages
CHANGELOG
LICENSE
README
Based on Rails SQL Views
Installation
Usage
Known Issues
Contributing
Rakefile
TODO
Classes/Modules
RailsSqlViews
RailsSqlViews::ConnectionAdapters
RailsSqlViews::ConnectionAdapters::AbstractAdapter
RailsSqlViews::ConnectionAdapters::MappingDefinition
RailsSqlViews::ConnectionAdapters::Mysql2Adapter
RailsSqlViews::ConnectionAdapters::MysqlAdapter
RailsSqlViews::ConnectionAdapters::OciAdapter
RailsSqlViews::ConnectionAdapters::OracleAdapter
RailsSqlViews::ConnectionAdapters::OracleEnhancedAdapter
RailsSqlViews::ConnectionAdapters::PostgreSQLAdapter
RailsSqlViews::ConnectionAdapters::SQLServerAdapter
RailsSqlViews::ConnectionAdapters::SQLiteAdapter
RailsSqlViews::ConnectionAdapters::SchemaStatements
RailsSqlViews::ConnectionAdapters::ViewDefinition
RailsSqlViews::Loader
RailsSqlViews::SchemaDumper
Test
Test::Unit
Test::Unit::TestCase
ActiveRecord
ActiveRecord::View
AdapterTest
Item
Module
Object
Person
Person2
Place
SchemaDumperTest
VPerson
VProfile
ViewModelTest
ViewOperationsTest
Methods
::based_on — ActiveRecord::View
::clone_association — ActiveRecord::View
::included — RailsSqlViews::ConnectionAdapters::MysqlAdapter
::included — RailsSqlViews::ConnectionAdapters::Mysql2Adapter
::included — RailsSqlViews::SchemaDumper
::included — RailsSqlViews::ConnectionAdapters::PostgreSQLAdapter
::included — RailsSqlViews::ConnectionAdapters::AbstractAdapter
::included — RailsSqlViews::ConnectionAdapters::SchemaStatements
::inherited — Object
::load_extensions — RailsSqlViews::Loader
::new — RailsSqlViews::ConnectionAdapters::ViewDefinition
::new — RailsSqlViews::ConnectionAdapters::MappingDefinition
#alias_method_chain — Module
#assert_dump_and_load_succeed — SchemaDumperTest
#base_tables — RailsSqlViews::ConnectionAdapters::SQLServerAdapter
#base_tables — RailsSqlViews::ConnectionAdapters::SQLiteAdapter
#base_tables — RailsSqlViews::ConnectionAdapters::PostgreSQLAdapter
#cleanup — ViewModelTest
#column — RailsSqlViews::ConnectionAdapters::ViewDefinition
#create_mapping_view — RailsSqlViews::ConnectionAdapters::SchemaStatements
#create_people_view — Test::Unit::TestCase
#create_view — RailsSqlViews::ConnectionAdapters::SchemaStatements
#disable_referential_integrity_with_views_excluded — RailsSqlViews::ConnectionAdapters::AbstractAdapter
#drop_table_with_cascade — RailsSqlViews::ConnectionAdapters::SchemaStatements
#drop_view — RailsSqlViews::ConnectionAdapters::SchemaStatements
#dump_with_views — RailsSqlViews::SchemaDumper
#map_column — RailsSqlViews::ConnectionAdapters::MappingDefinition
#nonview_tables — RailsSqlViews::ConnectionAdapters::PostgreSQLAdapter
#nonview_tables — RailsSqlViews::ConnectionAdapters::SQLServerAdapter
#nonview_tables — RailsSqlViews::ConnectionAdapters::SQLiteAdapter
#readonly? — ActiveRecord::View
#select_cols — RailsSqlViews::ConnectionAdapters::MappingDefinition
#setup — ViewModelTest
#setup — SchemaDumperTest
#structure_dump — RailsSqlViews::ConnectionAdapters::MysqlAdapter
#structure_dump — RailsSqlViews::ConnectionAdapters::Mysql2Adapter
#supports_drop_table_cascade? — RailsSqlViews::ConnectionAdapters::SQLiteAdapter
#supports_drop_table_cascade? — RailsSqlViews::ConnectionAdapters::AbstractAdapter
#supports_view_columns_definition? — RailsSqlViews::ConnectionAdapters::SQLiteAdapter
#supports_view_columns_definition? — RailsSqlViews::ConnectionAdapters::AbstractAdapter
#supports_views? — RailsSqlViews::ConnectionAdapters::AbstractAdapter
#supports_views? — RailsSqlViews::ConnectionAdapters::MysqlAdapter
#supports_views? — RailsSqlViews::ConnectionAdapters::Mysql2Adapter
#supports_views? — RailsSqlViews::ConnectionAdapters::SQLServerAdapter
#supports_views? — RailsSqlViews::ConnectionAdapters::OciAdapter
#supports_views? — RailsSqlViews::ConnectionAdapters::OracleAdapter
#supports_views? — RailsSqlViews::ConnectionAdapters::OracleEnhancedAdapter
#supports_views? — RailsSqlViews::ConnectionAdapters::PostgreSQLAdapter
#supports_views? — RailsSqlViews::ConnectionAdapters::SQLiteAdapter
#tables_with_views_excluded — RailsSqlViews::SchemaDumper
#tables_with_views_included — RailsSqlViews::ConnectionAdapters::PostgreSQLAdapter
#tables_with_views_included — RailsSqlViews::ConnectionAdapters::Mysql2Adapter
#tables_with_views_included — RailsSqlViews::ConnectionAdapters::MysqlAdapter
#teardown — SchemaDumperTest
#test_access_cloned_belongs_to_association — ViewModelTest
#test_access_cloned_habtm_association — ViewModelTest
#test_access_cloned_has_many_association — ViewModelTest
#test_base_tables — AdapterTest
#test_cloned_belongs_to_association_exists — ViewModelTest
#test_cloned_habtm_association_exists — ViewModelTest
#test_cloned_has_many_association_exists — ViewModelTest
#test_columns — AdapterTest
#test_create_view — ViewOperationsTest
#test_current_database — AdapterTest
#test_drop_view — ViewOperationsTest
#test_dump_and_load — SchemaDumperTest
#test_logging_error — SchemaDumperTest
#test_mapped_columns — AdapterTest
#test_mapped_views — AdapterTest
#test_no_view_raises_error — ViewOperationsTest
#test_non_allowed_object_raises_error — SchemaDumperTest
#test_old_name_not_found_error_during_mapping — AdapterTest
#test_regex_ignore — SchemaDumperTest
#test_same_person — ViewModelTest
#test_supports_views — AdapterTest
#test_symbol_ignore — SchemaDumperTest
#test_tables — AdapterTest
#test_union — SchemaDumperTest
#test_view — SchemaDumperTest
#test_view_creation_order — SchemaDumperTest
#test_view_select_statement — AdapterTest
#test_views — AdapterTest
#to_sql — RailsSqlViews::ConnectionAdapters::ViewDefinition
#trailer_with_views — RailsSqlViews::SchemaDumper
#view — RailsSqlViews::SchemaDumper
#view_cols — RailsSqlViews::ConnectionAdapters::MappingDefinition
#view_select_statement — RailsSqlViews::ConnectionAdapters::Mysql2Adapter
#view_select_statement — RailsSqlViews::ConnectionAdapters::SQLiteAdapter
#view_select_statement — RailsSqlViews::ConnectionAdapters::MysqlAdapter
#view_select_statement — RailsSqlViews::ConnectionAdapters::AbstractAdapter
#view_select_statement — RailsSqlViews::ConnectionAdapters::PostgreSQLAdapter
#view_select_statement — RailsSqlViews::ConnectionAdapters::SQLServerAdapter
#view_select_statement — RailsSqlViews::ConnectionAdapters::OracleAdapter
#view_select_statement — RailsSqlViews::ConnectionAdapters::OciAdapter
#view_select_statement — RailsSqlViews::ConnectionAdapters::OracleEnhancedAdapter
#views — RailsSqlViews::ConnectionAdapters::AbstractAdapter
#views — RailsSqlViews::SchemaDumper
#views — RailsSqlViews::ConnectionAdapters::SQLServerAdapter
#views — RailsSqlViews::ConnectionAdapters::SQLiteAdapter


Now Pythia controls your video contents.Problem solved, right?
We already live in the misinformation era. Technological AI development will allow an increasingly easy manipulation of media, and humans with AI skills will be able to generate completely synthetic videos. We can easily imagine a future where we won't be able to tell the difference between what is true and what is not. Given this scenario, do you think we will need more control over online content?
Yes, we need someone controlling online content, solving the problem from the core.
No, "freedom of expression" means accepting that too.
- Albert Einstein


{% extends "base.html" %} {% block content %} {% load static %} {% include "navbar.html" %}
OVERALL RATING
WORKLOAD
LEARNING
GRADING
REVIEWS


Join our Community
We're happy to have you as a part of the PaperMC community!Discord
Discord is a popular option for many gamers to communicate with each other. We have a Discord community server that anyone can join.
chat
IRC
IRC and Discord are bridged so it's completely up to you which you want to use.
You can join us on either of these two servers:
#paper on irc.spi.gt
#paper on irc.esper.net