skycap 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ skycap
2
+ ======
3
+
4
+ A simple JavaScript tracker & server for Sky.
data/bin/skycap ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
3
+
4
+ require 'rubygems'
5
+ require 'skycap'
6
+ require 'skydb'
7
+ require 'commander/import'
8
+
9
+ ################################################################################
10
+ # Initialization
11
+ ################################################################################
12
+
13
+ # CLI Setup
14
+ program :name, 'Skycap'
15
+ program :version, Skycap::VERSION
16
+ program :description, 'Sky Event Capture Server.'
17
+ default_command :server
18
+
19
+ # Catch signals
20
+ Signal.trap('TERM') { Process.kill('KILL', 0) }
21
+
22
+
23
+ ################################################################################
24
+ # Commands
25
+ ################################################################################
26
+
27
+ command :server do |c|
28
+ c.syntax = 'skycap server'
29
+ c.description = 'Runs the Skycap server.'
30
+ c.option '--debug', 'Turns on debugging.'
31
+ c.option '--port PORT', Integer, 'HTTP server port'
32
+ c.option '--table NAME', String, 'The name of the Sky table to save to.'
33
+ c.option '--sky-host HOST', String, 'The host name of the Sky server.'
34
+ c.option '--sky-port PORT', Integer, 'The port number of the Sky server.'
35
+ c.action do |args, options|
36
+ options.default(:port => 10001)
37
+
38
+ # Make sure a table is specified.
39
+ abort('Table required.') if options.table.nil?
40
+ SkyDB.table_name = options.table
41
+
42
+ # Check to make sure that Sky is running.
43
+ SkyDB.host = options.sky_host unless options.sky_host.nil?
44
+ SkyDB.port = options.sky_port unless options.sky_port.nil?
45
+ abort('Sky is not available.') unless SkyDB.ping
46
+
47
+ SkyDB.debug = true if options.debug
48
+
49
+ # Run the server.
50
+ Skycap::App.run!(:port => options.port)
51
+ end
52
+ end
53
+
@@ -0,0 +1,10 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Skycap</title>
6
+ <script src="/skycap.js"></script>
7
+ </head>
8
+ <body>
9
+ </body>
10
+ </html>
@@ -0,0 +1,190 @@
1
+ (function() {
2
+ var skycap = {
3
+ //--------------------------------------------------------------------------
4
+ //
5
+ // Properties
6
+ //
7
+ //--------------------------------------------------------------------------
8
+
9
+ // The object identifier.
10
+ objectId : null,
11
+
12
+ // The object-level data.
13
+ data : {},
14
+
15
+ // The pending action to be sent once the library is initialized.
16
+ pendingAction : null,
17
+ pendingCallback : null,
18
+
19
+ // A flag stating if object level data has been sent to the server yet.
20
+ // Object level data will be sent with the first event or will be sent
21
+ // by itself if no event is available after initialization.
22
+ initialized : false,
23
+
24
+
25
+ //--------------------------------------------------------------------------
26
+ //
27
+ // Methods
28
+ //
29
+ //--------------------------------------------------------------------------
30
+
31
+ //----------------------------------
32
+ // Identification
33
+ //----------------------------------
34
+
35
+ /**
36
+ * Identifies the current object with an identifier.
37
+ *
38
+ * @param {String} objectId The object identifier.
39
+ * @param {Object} data Object-level data.
40
+ */
41
+ identify : function(objectId, data) {
42
+ this.objectId = objectId;
43
+ this.data = data;
44
+
45
+ if(this.initialized) {
46
+ this.send();
47
+ }
48
+ },
49
+
50
+
51
+ //----------------------------------
52
+ // Tracking
53
+ //----------------------------------
54
+
55
+ /**
56
+ * Stores a single action and related data.
57
+ *
58
+ * @param {String} name The name of the action to track.
59
+ * @param {Object} properties The action properties.
60
+ */
61
+ track : function(name, properties, callback) {
62
+ if(typeof(properties) != "object") properties = {};
63
+ action = this.extend({}, properties, {name:name});
64
+
65
+ // If the library has been initialized then send it.
66
+ if(this.initialized) {
67
+ return this.send(action, callback)
68
+ }
69
+ // Otherwise wait for initialization and it'll be sent later.
70
+ else {
71
+ this.pendingAction = action;
72
+ this.pendingCallback = callback;
73
+ }
74
+ },
75
+
76
+ /**
77
+ * Sends an event with the current identity, data and relavent action.
78
+ *
79
+ * @param {Object} action The action object to send.
80
+ */
81
+ send : function(action, callback) {
82
+ if(!this.objectId) {
83
+ log("[skycap] Cannot track without object identifier.");
84
+ return;
85
+ }
86
+
87
+ var event = {objectId:this.objectId};
88
+ if(typeof(this.data) == "object") event.data = this.data;
89
+ if(typeof(action) == "object") event.action = action;
90
+ return this.post("/track", event, callback);
91
+ },
92
+
93
+
94
+
95
+ //----------------------------------
96
+ // Utility
97
+ //----------------------------------
98
+
99
+ /**
100
+ * Logs to the console if one is available.
101
+ */
102
+ log : function() {
103
+ if(window.console) {
104
+ console.log.apply(console, arguments);
105
+ }
106
+ },
107
+
108
+ /**
109
+ * Sends a JSON object over XHR POST.
110
+ *
111
+ * @param {String} url The url to send to.
112
+ * @param {Object} data The object to convert to JSON and send.
113
+ * @param {Function} callback The function to call when successfully executed.
114
+ *
115
+ * @return {XMLHTTPRequest} The XHR that was created.
116
+ */
117
+ post : function(url, data, callback) {
118
+ if(!window.XMLHttpRequest) return;
119
+ var xhr = new XMLHttpRequest();
120
+ var self = this;
121
+
122
+ // Setup callback.
123
+ xhr.onreadystatechange = function() {
124
+ if(xhr.readyState == 4) {
125
+ var response = {};
126
+ try { response = JSON.parse(xhr.responseText); } catch(e){}
127
+
128
+ if(xhr.status == 200 && response.status == "ok") {
129
+ if(typeof(callback) == "function") {
130
+ callback();
131
+ }
132
+ }
133
+ else {
134
+ self.log("[skycap] (" + url + ") " + response.message, data);
135
+ }
136
+ }
137
+ }
138
+
139
+ // Send request.
140
+ xhr.open("POST", url, true);
141
+ xhr.setRequestHeader("Content-type", "application/json");
142
+ xhr.setRequestHeader("Cache-Control", "no-cache");
143
+ xhr.send(JSON.stringify(data));
144
+ },
145
+
146
+ /**
147
+ * Merges the properties of multiple objects together.
148
+ * Original: https://github.com/documentcloud/underscore/blob/master/underscore.js
149
+ *
150
+ * @param {Object} obj The object to extend.
151
+ */
152
+ extend : function(obj) {
153
+ if(typeof(obj) != "object") return;
154
+
155
+ var args = Array.prototype.slice.call(arguments, 1);
156
+ for(var i=0, source; source = args[i]; i++) {
157
+ if (typeof(source) == "object") {
158
+ for (var property in source) {
159
+ obj[property] = source[property];
160
+ }
161
+ }
162
+ }
163
+ return obj;
164
+ },
165
+
166
+ /**
167
+ * Performs a shallow clone of an object.
168
+ */
169
+ clone : function(obj) {
170
+ if(!obj) return;
171
+ return this.extend({}, obj);
172
+ },
173
+ };
174
+
175
+ // Wrap existing onload.
176
+ var onload = window.onload;
177
+ window.onload = function() {
178
+ skycap.initialized = true;
179
+
180
+ // Call existing onload handler.
181
+ if(typeof(onload) == "function") onload();
182
+
183
+ // Delay first action until after initialization.
184
+ if(skycap.data != null || skycap.pendingAction != null) {
185
+ skycap.send(skycap.pendingAction, skycap.pendingCallback);
186
+ }
187
+ }
188
+
189
+ window.skycap = skycap;
190
+ })();
data/lib/skycap/app.rb ADDED
@@ -0,0 +1,77 @@
1
+ require 'sinatra/base'
2
+ require 'json'
3
+ require 'date'
4
+
5
+ class Skycap
6
+ class App < Sinatra::Base
7
+ ############################################################################
8
+ #
9
+ # Configuration
10
+ #
11
+ ############################################################################
12
+
13
+ configure do
14
+ enable :logging
15
+ enable :static
16
+ set :public_folder, File.dirname(__FILE__) + '/app/static'
17
+ end
18
+
19
+
20
+ ############################################################################
21
+ #
22
+ # Routes
23
+ #
24
+ ############################################################################
25
+
26
+ # Tracks a single event.
27
+ post '/track' do
28
+ content_type 'application/json'
29
+
30
+ # Parse the body into a parameters.
31
+ begin
32
+ params = JSON.parse(request.env["rack.input"].read)
33
+ rescue JSON::ParserError => e
34
+ halt 400, {status:'error', message:'Invalid JSON in body.'}.to_json
35
+ end
36
+
37
+ # Validate
38
+ halt 422, {status:'error', message:'Identifier required.'}.to_json if params['objectId'].blank?
39
+
40
+ if params['action'].is_a?(Hash)
41
+ halt 422, {status:'error', message:'Action name required.'}.to_json if params['action']['name'].nil?
42
+ else
43
+ halt 422, {status:'error', message:'Action object or data object required.'}.to_json unless params['data'].is_a?(Hash)
44
+ end
45
+
46
+ # Parse timestamp.
47
+ if params['timestamp'].blank?
48
+ params['timestamp'] = Time.now
49
+ else
50
+ begin
51
+ params['timestamp'] = Time.iso8601(params['timestamp'])
52
+ rescue ArgumentError => e
53
+ halt 400, {status:'error', message:'Invalid timestamp format.'}.to_json
54
+ end
55
+ end
56
+
57
+ # Create Sky event.
58
+ event = SkyDB::Event.new({
59
+ :object_id => params['objectId'],
60
+ :timestamp => params['timestamp']
61
+ })
62
+ event.action = params['action'] unless params['action'].nil?
63
+ event.data = params['data'] unless params['data'].nil?
64
+
65
+ # Send Sky event.
66
+ begin
67
+ SkyDB.add_event(event)
68
+ rescue StandardError => e
69
+ logger.error("#{e.class}: #{e.message}\n#{e.backtrace}")
70
+ halt 500, {status:'error', message:e.message}.to_json
71
+ end
72
+
73
+ # Send an ok if everything worked.
74
+ {status:'ok'}.to_json
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,5 @@
1
+ class NilClass
2
+ def blank?
3
+ return true
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class String
2
+ def blank?
3
+ return self == ''
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ class Skycap
2
+ VERSION = "0.2.3"
3
+ end
data/lib/skycap.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'skydb'
2
+
3
+ require 'skycap/app'
4
+ require 'skycap/version'
5
+
6
+ require 'skycap/ext/nil'
7
+ require 'skycap/ext/string'
8
+
9
+ class Skycap
10
+ end
metadata ADDED
@@ -0,0 +1,301 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: skycap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ben Johnson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sinatra
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.3.3
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.3.3
30
+ - !ruby/object:Gem::Dependency
31
+ name: thin
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.5.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.5.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: commander
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 4.1.2
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 4.1.2
62
+ - !ruby/object:Gem::Dependency
63
+ name: skydb
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 0.2.3
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 0.2.3
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 10.0.3
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 10.0.3
94
+ - !ruby/object:Gem::Dependency
95
+ name: minitest
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: 4.3.3
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: 4.3.3
110
+ - !ruby/object:Gem::Dependency
111
+ name: mocha
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: 0.13.1
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ version: 0.13.1
126
+ - !ruby/object:Gem::Dependency
127
+ name: rerun
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ version: 0.7.1
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ version: 0.7.1
142
+ - !ruby/object:Gem::Dependency
143
+ name: rb-fsevent
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ~>
148
+ - !ruby/object:Gem::Version
149
+ version: 0.9.1
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ~>
156
+ - !ruby/object:Gem::Version
157
+ version: 0.9.1
158
+ - !ruby/object:Gem::Dependency
159
+ name: rack-test
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ~>
164
+ - !ruby/object:Gem::Version
165
+ version: 0.6.2
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ~>
172
+ - !ruby/object:Gem::Version
173
+ version: 0.6.2
174
+ - !ruby/object:Gem::Dependency
175
+ name: unindentable
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ~>
180
+ - !ruby/object:Gem::Version
181
+ version: 0.1.0
182
+ type: :development
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ~>
188
+ - !ruby/object:Gem::Version
189
+ version: 0.1.0
190
+ - !ruby/object:Gem::Dependency
191
+ name: timecop
192
+ requirement: !ruby/object:Gem::Requirement
193
+ none: false
194
+ requirements:
195
+ - - ~>
196
+ - !ruby/object:Gem::Version
197
+ version: 0.5.9.2
198
+ type: :development
199
+ prerelease: false
200
+ version_requirements: !ruby/object:Gem::Requirement
201
+ none: false
202
+ requirements:
203
+ - - ~>
204
+ - !ruby/object:Gem::Version
205
+ version: 0.5.9.2
206
+ - !ruby/object:Gem::Dependency
207
+ name: simplecov
208
+ requirement: !ruby/object:Gem::Requirement
209
+ none: false
210
+ requirements:
211
+ - - ~>
212
+ - !ruby/object:Gem::Version
213
+ version: 0.7.1
214
+ type: :development
215
+ prerelease: false
216
+ version_requirements: !ruby/object:Gem::Requirement
217
+ none: false
218
+ requirements:
219
+ - - ~>
220
+ - !ruby/object:Gem::Version
221
+ version: 0.7.1
222
+ - !ruby/object:Gem::Dependency
223
+ name: m
224
+ requirement: !ruby/object:Gem::Requirement
225
+ none: false
226
+ requirements:
227
+ - - ~>
228
+ - !ruby/object:Gem::Version
229
+ version: 1.3.1
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ none: false
234
+ requirements:
235
+ - - ~>
236
+ - !ruby/object:Gem::Version
237
+ version: 1.3.1
238
+ - !ruby/object:Gem::Dependency
239
+ name: pry
240
+ requirement: !ruby/object:Gem::Requirement
241
+ none: false
242
+ requirements:
243
+ - - ~>
244
+ - !ruby/object:Gem::Version
245
+ version: 0.9.12
246
+ type: :development
247
+ prerelease: false
248
+ version_requirements: !ruby/object:Gem::Requirement
249
+ none: false
250
+ requirements:
251
+ - - ~>
252
+ - !ruby/object:Gem::Version
253
+ version: 0.9.12
254
+ description:
255
+ email:
256
+ - benbjohnson@yahoo.com
257
+ executables:
258
+ - skycap
259
+ extensions: []
260
+ extra_rdoc_files: []
261
+ files:
262
+ - lib/skycap/app/static/index.html
263
+ - lib/skycap/app/static/skycap.js
264
+ - lib/skycap/app.rb
265
+ - lib/skycap/ext/nil.rb
266
+ - lib/skycap/ext/string.rb
267
+ - lib/skycap/version.rb
268
+ - lib/skycap.rb
269
+ - README.md
270
+ - bin/skycap
271
+ homepage: http://github.com/skydb/skycap
272
+ licenses: []
273
+ post_install_message:
274
+ rdoc_options: []
275
+ require_paths:
276
+ - lib
277
+ required_ruby_version: !ruby/object:Gem::Requirement
278
+ none: false
279
+ requirements:
280
+ - - ! '>='
281
+ - !ruby/object:Gem::Version
282
+ version: '0'
283
+ segments:
284
+ - 0
285
+ hash: 1542402633902659861
286
+ required_rubygems_version: !ruby/object:Gem::Requirement
287
+ none: false
288
+ requirements:
289
+ - - ! '>='
290
+ - !ruby/object:Gem::Version
291
+ version: '0'
292
+ segments:
293
+ - 0
294
+ hash: 1542402633902659861
295
+ requirements: []
296
+ rubyforge_project:
297
+ rubygems_version: 1.8.25
298
+ signing_key:
299
+ specification_version: 3
300
+ summary: Sky Event Capture Server
301
+ test_files: []