JS: Handle account events through Account class.

This commit is contained in:
Stefan Thomas
2013-02-12 18:35:30 +01:00
parent 540b52081e
commit cfe76ef92e
3 changed files with 174 additions and 7 deletions

View File

@@ -10,16 +10,75 @@
// var network = require("./network.js");
var EventEmitter = require('events').EventEmitter;
var Amount = require('./amount.js').Amount;
var UInt160 = require('./amount.js').UInt160;
var Amount = require('./amount').Amount;
var UInt160 = require('./uint160').UInt160;
var Account = function (network, account) {
this._network = network;
this._account = UInt160.json_rewrite(account);
var Account = function (remote, account) {
var self = this;
this._remote = remote;
this._account = UInt160.from_json(account);
// Ledger entry object
// Important: This must never be overwritten, only extend()-ed
this._entry = {};
this.on('newListener', function (type, listener) {
if (Account.subscribe_events.indexOf(type) !== -1) {
if (!this._subs && 'open' === this._remote._online_state) {
this._remote.request_subscribe()
.accounts(this._account.to_json())
.request();
}
this._subs += 1;
}
});
this.on('removeListener', function (type, listener) {
if (Account.subscribe_events.indexOf(type) !== -1) {
this._subs -= 1;
if (!this._subs && 'open' === this._remote._online_state) {
this._remote.request_unsubscribe([ 'transactions' ])
.accounts(this._account.to_json())
.request();
}
}
});
this._remote.on('connect', function () {
if (self._subs) {
this._remote.request_subscribe()
.accounts(this._account.to_json())
.request();
}
});
return this;
};
Account.prototype = new EventEmitter;
/**
* List of events that require a remote subscription to the account.
*/
Account.subscribe_events = ['transaction', 'entry'];
Account.prototype.to_json = function ()
{
return this._account.to_json();
};
/**
* Whether the AccountId is valid.
*
* Note: This does not tell you whether the account exists in the ledger.
*/
Account.prototype.is_valid = function ()
{
return this._account.is_valid();
};
exports.Account = Account;
// vim:sw=2:sts=2:ts=8:et

76
src/js/meta.js Normal file
View File

@@ -0,0 +1,76 @@
var extend = require('extend');
/**
* Meta data processing facility.
*/
var Meta = function (raw_data)
{
this.nodes = [];
for (var i = 0, l = raw_data.AffectedNodes.length; i < l; i++) {
var an = raw_data.AffectedNodes[i],
result = {};
["CreatedNode", "ModifiedNode", "DeletedNode"].forEach(function (x) {
if (an[x]) result.diffType = x;
});
if (!result.diffType) return null;
an = an[result.diffType];
result.entryType = an.LedgerEntryType;
result.ledgerIndex = an.LedgerIndex;
result.fields = extend({}, an.PreviousFields, an.NewFields, an.FinalFields);
result.fieldsPrev = an.PreviousFields || {};
result.fieldsNew = an.NewFields || {};
result.fieldsFinal = an.FinalFields || {};
this.nodes.push(result);
}
};
/**
* Execute a function on each affected node.
*
* The callback is passed two parameters. The first is a node object which looks
* like this:
*
* {
* // Type of diff, e.g. CreatedNode, ModifiedNode
* diffType: 'CreatedNode'
*
* // Type of node affected, e.g. RippleState, AccountRoot
* entryType: 'RippleState',
*
* // Index of the ledger this change occurred in
* ledgerIndex: '01AB01AB...',
*
* // Contains all fields with later versions taking precedence
* //
* // This is a shorthand for doing things like checking which account
* // this affected without having to check the diffType.
* fields: {...},
*
* // Old fields (before the change)
* fieldsPrev: {...},
*
* // New fields (that have been added)
* fieldsNew: {...},
*
* // Changed fields
* fieldsFinal: {...}
* }
*
* The second parameter to the callback is the index of the node in the metadata
* (first entry is index 0).
*/
Meta.prototype.each = function (fn)
{
for (var i = 0, l = this.nodes.length; i < l; i++) {
fn(this.nodes[i], i);
}
}
exports.Meta = Meta;

View File

@@ -20,6 +20,8 @@ var Amount = require('./amount').Amount;
var Currency = require('./amount').Currency;
var UInt160 = require('./amount').UInt160;
var Transaction = require('./transaction').Transaction;
var Account = require('./account').Account;
var Meta = require('./meta').Meta;
var utils = require('./utils');
var config = require('./config');
@@ -233,6 +235,7 @@ var Remote = function (opts, trace) {
}
// Cache information for accounts.
// DEPRECATED, will be removed
this.accounts = {
// Consider sequence numbers stable if you know you're not generating bad transactions.
// Otherwise, clear it to have it automatically refreshed from the network.
@@ -241,6 +244,9 @@ var Remote = function (opts, trace) {
};
// Hash map of Account objects by AccountId.
this._accounts = {};
// List of secrets that we know about.
this.secrets = {
// Secrets can be set by calling set_secret(account, secret).
@@ -507,6 +513,7 @@ Remote.prototype._connect_start = function () {
// It is possible for messages to be dispatched after the connection is closed.
Remote.prototype._connect_message = function (ws, json) {
var self = this;
var message = JSON.parse(json);
var unexpected = false;
var request;
@@ -557,6 +564,23 @@ Remote.prototype._connect_message = function (ws, json) {
case 'account':
// XXX If not trusted, need proof.
// Process metadata
message.mmeta = new Meta(message.meta);
// Pass the event on to any related Account objects
message.mmeta.each(function (an) {
if (an.entryType === 'AccountRoot') {
var account = self._accounts[an.fields.Account];
// Only trigger the event if the account object is actually
// subscribed - this prevents some weird phantom events from
// occurring.
if (account && account._subs) {
account.emit('transaction', message);
}
}
});
this.emit('account', message);
break;
@@ -994,6 +1018,14 @@ Remote.prototype.request_owner_count = function (account, current) {
});
};
Remote.prototype.account = function (accountId) {
var account = new Account(this, accountId);
if (!account.is_valid()) return account;
return this._accounts[account.to_json()] = account;
};
// Return the next account sequence if possible.
// <-- undefined or Sequence
Remote.prototype.account_seq = function (account, advance) {