Skip to content
Snippets Groups Projects
Commit 204e1ab5 authored by Devon Carew's avatar Devon Carew
Browse files

work on a ga library

parent 83af3333
No related branches found
No related tags found
No related merge requests found
// Copyright (c) 2014, Google Inc. Please see the AUTHORS file for details.
// All rights reserved. Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/**
* TODO:
*/
library stagehand.analytics;
import 'dart:async';
import 'dart:math' as math;
// TODO: Under construction!
// TODO: store a generated anonymous client id
// TODO: store whether the user has opted in / out
// https://developers.google.com/analytics/devguides/collection/protocol/policy
class Analytics {
static const String _GA_URL = 'https://www.google-analytics.com/collect';
/// Tracking ID / Property ID.
final String trackingId;
final PropertiesHandler propertiesHandler;
final PostHandler postHandler;
final String applicationVersion;
final String applicationId;
/// Anonymous Client ID. The value of this field should be a random UUID
/// (version 4).
String _clientId;
final _ThrottlingBucket _bucket = new _ThrottlingBucket(20);
Analytics(this.trackingId, this.propertiesHandler, this.postHandler,
{this.applicationVersion, this.applicationId}) {
assert(trackingId != null);
}
// Future sendPageView() {
//
// }
Future sendScreenView() {
// TODO:
Map args = {};
return _sendPayload('screenview', args);
}
Future sendEvent() {
// TODO:
return new Future.value();
}
Future sendException(String description, [bool fatal]) {
Map args = {'exd': description};
if (fatal != null && fatal) args['exf'] = '1';
return _sendPayload('exception', args);
}
// Valid values for [hitType] are: 'pageview', 'screenview', 'event',
// 'transaction', 'item', 'social', 'exception', and 'timing'.
Future _sendPayload(String hitType, Map args) {
assert(hitType != null);
if (_bucket.removeDrop()) {
args['v'] = '1'; // version
args['tid'] = trackingId;
args['cid'] = _clientId;
args['t'] = hitType;
if (applicationId != null) args['aid'] = applicationId;
if (applicationVersion != null) args['av'] = applicationVersion;
return postHandler.sendPost(_GA_URL, args);
} else {
return new Future.value();
}
}
}
/**
* TODO:
*/
abstract class PropertiesHandler {
dynamic getProperty(String key);
void setProperty(String key, dynamic value);
}
/**
* TODO:
*/
abstract class PostHandler {
/**
* TODO:
*/
Future sendPost(String url, Map<String, String> parameters);
}
/**
* A throttling algorithim. This models the throttling after a bucket with
* water dripping into it at the rate of 1 drop per second. If the bucket has
* water when an operation is requested, 1 drop of water is removed and the
* operation is performed. If not the operation is skipped. This algorithim
* lets operations be peformed in bursts without throttling, but holds the
* overall average rate of operations to 1 per second.
*/
class _ThrottlingBucket {
final int startingCount;
int drops;
int _lastReplenish;
_ThrottlingBucket(this.startingCount) {
drops = startingCount;
_lastReplenish = new DateTime.now().millisecondsSinceEpoch;
}
bool removeDrop() {
_checkReplenish();
if (drops <= 0) {
return false;
} {
drops--;
return true;
}
}
void _checkReplenish() {
int now = new DateTime.now().millisecondsSinceEpoch;
if (_lastReplenish + 1000 >= now) {
int inc = (now - _lastReplenish) ~/ 1000;
drops = math.min(drops + inc, startingCount);
_lastReplenish += (1000 * inc);
}
}
}
// Copyright (c) 2014, Google Inc. Please see the AUTHORS file for details.
// All rights reserved. Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
library stagehand.properties;
import 'dart:async';
/**
* TODO: doc
*/
abstract class Properties {
final String name;
final Map<String, dynamic> _map = {};
bool dirty = false;
Properties(this.name);
/**
* Get the value for the given key. This is an alias for the index operator to
* aid in discoverability.
*/
dynamic getValue(String key) => this[key];
/**
* Set the value for the given key. This is an alias for the index operator to
* aid in discoverability.
*/
void setValue(String key, dynamic value) => this[key] = value;
dynamic operator[](String key) => _map[key];
void operator[]=(String key, dynamic value) {
dirty = true;
_map[key] = value;
}
Future flush();
Map<String, dynamic> toMap() => new Map.from(_map);
String toString() => '${name} properties';
}
// Copyright (c) 2014, Google Inc. Please see the AUTHORS file for details.
// All rights reserved. Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
library stagehand.properties_html;
import 'dart:async';
import 'dart:convert' show JSON;
import 'dart:html';
import 'properties.dart';
export 'properties.dart';
// TODO: test the html version
/**
* TODO: doc
*/
class PropertiesHtml extends Properties {
static Future<PropertiesHtml> create(String name) {
String str = window.localStorage[name];
if (str == null || str.isEmpty) str = '{}';
Map map = JSON.decode(str);
return new Future.value(new PropertiesHtml._(name, map));
}
PropertiesHtml._(String name, Map other) : super(name) {
// Copy the map.
other.forEach((key, value) => this[key] = value);
dirty = false;
}
Future flush() {
window.localStorage[name] = JSON.encode(toMap());
return new Future.value();
}
}
// Copyright (c) 2014, Google Inc. Please see the AUTHORS file for details.
// All rights reserved. Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
library stagehand.properties_io;
import 'dart:async';
import 'dart:convert' show JSON;
import 'dart:io';
import 'package:path/path.dart' as path;
import 'properties.dart';
export 'properties.dart';
/**
* TODO: doc
*/
class PropertiesIo extends Properties {
static Future<PropertiesIo> create(String name) {
String filename = '.${name.replaceAll(' ', '_')}';
File file = new File(path.join(_userHomeDir(), filename));
return file.create().then((_) {
return file.readAsString();
}).then((String contents) {
if (contents.isEmpty) contents = '{}';
Map map = JSON.decode(contents);
return new PropertiesIo._(name, file, map);
});
}
final File file;
PropertiesIo._(String name, this.file, Map other) : super(name) {
// Copy the map.
other.forEach((key, value) => this[key] = value);
dirty = false;
}
Future flush() {
return file.writeAsString(JSON.encode(toMap())).then((_) {
dirty = false;
});
}
}
String _userHomeDir() {
String envKey = Platform.operatingSystem == 'windows' ? 'APPDATA' : 'HOME';
String value = Platform.environment[envKey];
return value == null ? '.' : value;
}
......@@ -5,7 +5,8 @@ homepage: https://github.com/sethladd/stagehand
authors:
- Seth Ladd <sethladd@gmail.com>
- Devon Carew <devoncarew@google.com>
dev_dependencies:
dependencies:
args: any
path: any
dev_dependencies:
unittest: any
......@@ -5,12 +5,11 @@
import 'cli_test.dart' as cli_test;
import 'common_test.dart' as common_test;
import 'generators_test.dart' as generators_test;
// TODO: integration tests. generate all the samples, run the analyzer over the
// dart code
import 'properties_io_test.dart' as properties_io_test;
void main() {
cli_test.defineTests();
common_test.defineTests();
generators_test.defineTests();
properties_io_test.defineTests();
}
// Copyright (c) 2014, Google Inc. Please see the AUTHORS file for details.
// All rights reserved. Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
library stagehand.properties_io_test;
import 'package:stagehand/properties/properties_io.dart';
import 'package:unittest/unittest.dart';
void main() => defineTests();
void defineTests() {
group('properties_io', () {
test('create', () {
return PropertiesIo.create('test_foo');
});
test('set get', () {
return PropertiesIo.create('test_foo').then((Properties props) {
props['foo'] = 'bar';
expect(props['foo'], 'bar');
});
});
test('dirty', () {
return PropertiesIo.create('test_foo').then((Properties props) {
expect(props.dirty, false);
props['foo'] = 'bar';
expect(props.dirty, true);
});
});
});
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment