surfnperf 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c4c7403d39855f590d1dd5e2c2345237274bf602
4
+ data.tar.gz: 6e855ec75d1e8b0635c9e4ab5b6597518609da26
5
+ SHA512:
6
+ metadata.gz: 52b75f11f6a00ba94a4a5e2d246d26860038dd0d9beac2371468a9059394620c794e562c2ff804813897589bd4fb9b9312d300eaa128408b9f61ce54161cb807
7
+ data.tar.gz: a9aba799e9f43869e3a45d0eb82acc8f63c17640db5e6f2c7309f0834d9608a299a40165e5c11e6a2b29370e2da3002c596f288a11e5e77fcec1fb7e557da3bf
data/README.md ADDED
@@ -0,0 +1,126 @@
1
+ Surf-N-Perf
2
+ ==============
3
+
4
+ Micro-library for gathering frontend web page performance data.
5
+
6
+ [![Build Status](https://travis-ci.org/Comcast/Surf-N-Perf.svg?branch=master)](https://travis-ci.org/Comcast/Surf-N-Perf)
7
+
8
+ ## Usage
9
+
10
+ ### Including the code in your project
11
+
12
+ There are 2 pieces of code that need to be included in your webpage:
13
+
14
+ **1.** The following code must be included as high up in the source code of your base HTML document as possible, ideally right after the opening ```<head>``` tag:
15
+
16
+ ```html
17
+ <script>
18
+ var SURF_N_PERF = {
19
+ marks: {},
20
+ highResMarks: {}
21
+ };
22
+
23
+ SURF_N_PERF.marks.pageStart = (new Date()).getTime();
24
+
25
+ if(window.performance) {
26
+ if(window.performance.now) {
27
+ SURF_N_PERF.highResMarks.pageStart = window.performance.now();
28
+ }
29
+ if(window.performance.mark) {
30
+ window.performance.mark('pageStart');
31
+ }
32
+ }
33
+
34
+ SURF_N_PERF.setPageLoad = function() {
35
+ SURF_N_PERF.marks.loadEventEnd = (new Date()).getTime();
36
+
37
+ if(window.performance && window.performance.now) {
38
+ SURF_N_PERF.highResMarks.loadEventEnd = window.performance.now();
39
+ }
40
+ };
41
+
42
+ if(window.addEventListener) {
43
+ window.addEventListener('load', SURF_N_PERF.setPageLoad, false);
44
+ } else {
45
+ window.attachEvent('onload', SURF_N_PERF.setPageLoad);
46
+ }
47
+ </script>
48
+ ```
49
+
50
+ That provides support for the following:
51
+ - A "pageStart" mark for browsers that do not support [Navigation Timing](http://www.w3.org/TR/navigation-timing/) which can be used to compute durations from when the page first started loading (specifically, this mark falls between the [domLoading](http://www.w3.org/TR/navigation-timing/#dom-performancetiming-domloading) and [domInteractive](http://www.w3.org/TR/navigation-timing/#dom-performancetiming-dominteractive) attributes of Navigation Timing)
52
+ - "pageStart" marks for browsers that support [High Resolution Time](http://www.w3.org/TR/hr-time/) and/or [User Timing](http://www.w3.org/TR/user-timing/) so that "pageStart" can be used as a consistent starting point for duration calculations across all browsers regardless of their supported features
53
+ - A "loadEventEnd" mark for browsers that do not support [Navigation Timing](http://www.w3.org/TR/navigation-timing/) which can be used to compute durations from when the load event of the document is completed ([loadEventEnd](http://www.w3.org/TR/navigation-timing/#dom-performancetiming-loadend))
54
+ - A "loadEventEnd" [DOMHighResTimeStamp](http://www.w3.org/TR/hr-time/#sec-DOMHighResTimeStamp) mark for calculating high resolution durations between a Navigation Timing mark and a user mark in browsers that support [High Resolution Time](http://www.w3.org/TR/hr-time/) but don't support [User Timing](http://www.w3.org/TR/user-timing/)
55
+
56
+ **2.** Then just drop the [surfnperf.min.js](https://github.com/Comcast/Surf-N-Perf/blob/master/surfnperf.min.js) in your codebase and reference that JavaScript file in your HTML document. If you're using [RequireJS](http://requirejs.org/) or [Browserify](http://browserify.org/), it registers itself as 'surfnperf'.
57
+
58
+ ### Storing & Retrieving Performance Data
59
+
60
+ Details in the [JavaScript API](https://github.com/Comcast/Surf-N-Perf/wiki/JavaScript-API) page in the wiki
61
+
62
+ ## Ruby Project Integration
63
+
64
+ ### Using within a Rails project
65
+
66
+ The [surfnperf Ruby Gem](https://rubygems.org/gems/surfnperf) allows you to quickly & easily integrate Surf-N-Perf into your Rails projects. To include the necessary files, add `surfnperf` to your `Gemfile`:
67
+
68
+ ```ruby
69
+ gem 'surfnperf'
70
+ ```
71
+
72
+ After a `$ bundle install`, you'll be able to include the [main JavaScript file](https://github.com/Comcast/Surf-N-Perf/blob/master/surfnperf.js) in your JavaScript manifest by simply adding:
73
+
74
+ ```
75
+ //= require surfnperf
76
+ ```
77
+
78
+ The necessary script for the ```<head>``` of your HTML document is also available to you via a [partial template](http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials) that you can include in the appropriate layout file for your page, such as `app/views/layouts/application.html.erb` by simply adding this line:
79
+
80
+ ```erb
81
+ <%= render "surfnperf/head" %>
82
+ ```
83
+
84
+ Those 3 lines of code are all your need to get started using Surf-N-Perf in Rails!
85
+
86
+ ### Using within other Ruby projects that integrate with Sprockets
87
+
88
+ [Sprockets](https://github.com/sstephenson/sprockets) is what powers the [Asset Pipeline](http://guides.rubyonrails.org/asset_pipeline.html) in Rails, as well as other Ruby website tools such as [Middleman](https://middlemanapp.com/). For these other Ruby projects that use [Sprockets](https://middlemanapp.com/advanced/asset_pipeline/), integration is similar to the Rails instructions above, with one extra step:
89
+
90
+ Add `surfnperf` to your `Gemfile`:
91
+
92
+ ```ruby
93
+ gem 'surfnperf'
94
+ ```
95
+
96
+ After a `$ bundle install`, include [surfnperf.js](https://github.com/Comcast/Surf-N-Perf/blob/master/surfnperf.js) in your JavaScript manifest by adding:
97
+
98
+ ```
99
+ //= require surfnperf
100
+ ```
101
+
102
+ For now, you'll have to manually include the [necessary script](#including-the-code-in-your-project) for the ```<head>``` of your HTML document. If someone would like to update the Ruby Gem to work as a proper [Middleman Extension](https://middlemanapp.com/advanced/custom_extensions/), we'd happily accept a [pull request](https://github.com/Comcast/Surf-N-Perf/issues/35).
103
+
104
+ ## Running Tests & Other Development Tools
105
+
106
+ Tests are written in [Jasmine](http://jasmine.github.io/) and run with [Karma](http://karma-runner.github.io/)
107
+
108
+ Install the dependencies by executing this command from the root of your Surf-N-Perf project directory:
109
+
110
+ ```bash
111
+ $ npm install
112
+ ```
113
+
114
+ And then run the tests, JSHint, beautify your code & generate the minified file with:
115
+
116
+ ```bash
117
+ $ grunt dev
118
+ ```
119
+
120
+ By default, it will run the tests using [PhantomJS](http://phantomjs.org/). You can also run the tests in any browser by going to http://localhost:9876/
121
+
122
+ If you encounter an error related to Grunt, it may not be installed, so [go install it](http://gruntjs.com/getting-started).
123
+
124
+ ## License
125
+
126
+ Licensed under the [MIT License](https://github.com/Comcast/Surf-N-Perf/blob/master/LICENSE)
@@ -0,0 +1,31 @@
1
+ <script>
2
+ var SURF_N_PERF = {
3
+ marks: {},
4
+ highResMarks: {}
5
+ };
6
+
7
+ SURF_N_PERF.marks.pageStart = (new Date()).getTime();
8
+
9
+ if(window.performance) {
10
+ if(window.performance.now) {
11
+ SURF_N_PERF.highResMarks.pageStart = window.performance.now();
12
+ }
13
+ if(window.performance.mark) {
14
+ window.performance.mark('pageStart');
15
+ }
16
+ }
17
+
18
+ SURF_N_PERF.setPageLoad = function() {
19
+ SURF_N_PERF.marks.loadEventEnd = (new Date()).getTime();
20
+
21
+ if(window.performance && window.performance.now) {
22
+ SURF_N_PERF.highResMarks.loadEventEnd = window.performance.now();
23
+ }
24
+ };
25
+
26
+ if(window.addEventListener) {
27
+ window.addEventListener('load', SURF_N_PERF.setPageLoad, false);
28
+ } else {
29
+ window.attachEvent('onload', SURF_N_PERF.setPageLoad);
30
+ }
31
+ </script>
data/lib/surfnperf.rb ADDED
@@ -0,0 +1,21 @@
1
+ module SurfNPerf
2
+ class << self
3
+ def load!
4
+ if rails?
5
+ register_rails_engine
6
+ end
7
+ end
8
+
9
+ def rails?
10
+ defined?(::Rails)
11
+ end
12
+
13
+ private
14
+
15
+ def register_rails_engine
16
+ require 'surfnperf/rails'
17
+ end
18
+ end
19
+ end
20
+
21
+ SurfNPerf.load!
@@ -0,0 +1,6 @@
1
+ module SurfNPerf
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,361 @@
1
+ /*!
2
+ * Copyright 2015 Comcast Cable Communications Management, LLC
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all
12
+ * copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+ (function(root, factory) {
24
+ if(typeof define === 'function' && define.amd) {
25
+ // For Require.js
26
+ define('surfnperf', factory);
27
+ } else if(typeof exports === 'object') {
28
+ // For Browserify
29
+ module.exports = factory(require('surfnperf'));
30
+ } else {
31
+ // Browser global if not using Require.js or Browserify
32
+ root.surfnperf = factory();
33
+ }
34
+ }(this, function() {
35
+
36
+ /**
37
+ * Date.now() shim for older browsers
38
+ */
39
+ if(!Date.now) {
40
+ Date.now = function now() {
41
+ return new Date().getTime();
42
+ };
43
+ }
44
+
45
+ var defaults = function(o, d) {
46
+ for(var prop in d) {
47
+ if(!o.hasOwnProperty(prop)) {
48
+ o[prop] = d[prop];
49
+ }
50
+ }
51
+ },
52
+ contains = function(array, value) {
53
+ if(Array.prototype.indexOf) {
54
+ return array.indexOf(value) != -1;
55
+ } else {
56
+ var i, length = array.length;
57
+ for(i = 0; i < length; i++) {
58
+ if(array[i] === value) {
59
+ return true;
60
+ }
61
+ }
62
+ return false;
63
+ }
64
+ };
65
+
66
+ /**
67
+ * Frontend Web Performance Data Gathering
68
+ *
69
+ * @class SurfNPerf
70
+ */
71
+ var SurfNPerf = function() {
72
+ this._data = {
73
+ custom: {},
74
+ marks: {},
75
+ highResMarks: {},
76
+ events: {}
77
+ };
78
+
79
+ this._navigationTiming = null;
80
+ this._highResTime = null;
81
+ this._userTiming = null;
82
+
83
+ this._navigationTimingEvents = {
84
+ a: ["navigationStart", "unloadEventEnd", "unloadEventStart", "redirectStart", "redirectEnd", "fetchStart", "domainLookupStart", "domainLookupEnd", "connectStart", "secureConnectionStart", "connectEnd", "requestStart", "responseStart", "responseEnd", "domLoading"],
85
+ b: ["domInteractive", "domContentLoadedEventStart", "domContentLoadedEventEnd", "domComplete", "loadEventStart", "loadEventEnd"]
86
+ };
87
+
88
+ this.initialize();
89
+ },
90
+ SNPProto = SurfNPerf.prototype;
91
+
92
+ SNPProto._setPerformanceApis = function() {
93
+ if(window.performance) {
94
+ this._navigationTiming = !!(window.performance.timing);
95
+ this._highResTime = !!(window.performance.now);
96
+ this._userTiming = !!(window.performance.mark && window.performance.measure && window.performance.getEntriesByName);
97
+ } else {
98
+ this._navigationTiming = false;
99
+ this._highResTime = false;
100
+ this._userTiming = false;
101
+ }
102
+ };
103
+
104
+ SNPProto._setPerfProperties = function() {
105
+ if(!window.SURF_N_PERF || !window.SURF_N_PERF.marks) {
106
+ window.SURF_N_PERF = {
107
+ marks: {},
108
+ highResMarks: {}
109
+ };
110
+ }
111
+ };
112
+
113
+ SNPProto._setInitialUrl = function() {
114
+ this.setCustom('initialUrl', window.location.pathname);
115
+ };
116
+
117
+ SNPProto.initialize = function() {
118
+ this._setPerformanceApis();
119
+ this._setPerfProperties();
120
+ this._setInitialUrl();
121
+ };
122
+
123
+ /**
124
+ * Returns the timing data for a particular eventKey
125
+ *
126
+ * @arguments {String} timeType 'highRes' (to return a DOMHighResTimeStamp, if available) or 'DOM' (to return a DOMTimeStamp's value) - optional. Defaults to 'highRes'
127
+ * @returns {DOMHighResTimeStamp | integer} time value
128
+ * @memberOf SurfNPerf
129
+ */
130
+ SNPProto.now = function(timeType) {
131
+ timeType = timeType || 'highRes';
132
+ if(this._highResTime && timeType === 'highRes') {
133
+ return window.performance.now();
134
+ } else {
135
+ return Date.now();
136
+ }
137
+ };
138
+
139
+ SNPProto.performanceTiming = function() {
140
+ return this._navigationTiming ? window.performance.timing : {};
141
+ };
142
+
143
+ SNPProto._performanceTimingL2 = function(eventKey) {
144
+ var delta = this.getTimingMark('loadEventEnd', 'DOM') - this.getTimingMark(eventKey, 'DOM'),
145
+ value = window.SURF_N_PERF.highResMarks.loadEventEnd - delta;
146
+ return(value < 0) ? 0 : this._round(value, {
147
+ decimalPlaces: 10
148
+ });
149
+ };
150
+
151
+ /**
152
+ * Returns the timing data for a particular eventKey
153
+ *
154
+ * @arguments {String} eventKey name of the timing event
155
+ * @arguments {String} timeType 'highRes' (to return a DOMHighResTimeStamp, if available) or 'DOM' (to return a DOMTimeStamp's value) - optional. Defaults to 'DOM'
156
+ * @returns {DOMHighResTimeStamp | integer} time value
157
+ * @memberOf SurfNPerf
158
+ */
159
+ SNPProto.getTimingMark = function(eventKey, timeType) {
160
+ timeType = timeType || 'DOM';
161
+
162
+ if(this._navigationTiming) {
163
+ if(timeType === 'DOM' || this._highResTime === false) {
164
+ return this.performanceTiming()[eventKey];
165
+ } else { // timeType === 'HighRes'
166
+ return this._performanceTimingL2(eventKey);
167
+ }
168
+ } else {
169
+ if(contains(this._navigationTimingEvents.a, eventKey)) {
170
+ return this.getMark('pageStart', 'DOM');
171
+ } else {
172
+ return this.getMark('loadEventEnd', 'DOM');
173
+ }
174
+ }
175
+ };
176
+
177
+ SNPProto.userTiming = function() {
178
+ return this._userTiming ? window.performance : {};
179
+ };
180
+
181
+ SNPProto.mark = function(eventKey) {
182
+ if(this._highResTime) {
183
+ this._data.highResMarks[eventKey] = this.now();
184
+ }
185
+ if(this._userTiming) {
186
+ this.userTiming().mark(eventKey);
187
+ }
188
+ this._data.marks[eventKey] = this.now('DOM');
189
+ };
190
+
191
+ SNPProto.getMark = function(eventKey, timeType) {
192
+ var mark;
193
+
194
+ timeType = timeType || 'highRes';
195
+
196
+ if(timeType === 'highRes' && this._highResTime === true) {
197
+ mark = this._data.highResMarks[eventKey] || window.SURF_N_PERF.highResMarks[eventKey];
198
+ }
199
+ return mark || this._data.marks[eventKey] || window.SURF_N_PERF.marks[eventKey];
200
+ };
201
+
202
+ SNPProto._isTimingMark = function(eventKey) {
203
+ return contains(this._navigationTimingEvents.a.concat(this._navigationTimingEvents.b), eventKey);
204
+ };
205
+
206
+ SNPProto._getDurationMark = function(eventKey) {
207
+ if(this._isTimingMark(eventKey)) {
208
+ return this.getTimingMark(eventKey, 'highRes');
209
+ } else {
210
+ return this.getMark(eventKey);
211
+ }
212
+ };
213
+
214
+ SNPProto._round = function(n, options) {
215
+ options = options || {};
216
+ var dp = options.decimalPlaces || 0;
217
+ n = +(n);
218
+ return n.toFixed ? +(n).toFixed(dp) : n;
219
+ };
220
+
221
+ SNPProto._roundedDuration = function(a, b, options) {
222
+ return this._round(b - a, options);
223
+ };
224
+
225
+ SNPProto._measureName = function(a, b) {
226
+ return '_SNP_' + a + '_TO_' + b;
227
+ };
228
+
229
+ SNPProto._setMeasure = function(a, b) {
230
+ try {
231
+ this.userTiming().measure(this._measureName(a, b), a, b);
232
+ } catch(e) {
233
+ if(window.console && window.console.error) {
234
+ if(e && e.message) {
235
+ console.error("Surf-N-Perf Exception:", e.message);
236
+ } else {
237
+ console.error("Surf-N-Perf Exception: at least one of these events/marks is not available yet", a, b);
238
+ }
239
+ }
240
+ }
241
+ };
242
+
243
+ SNPProto._getMeasureDuration = function(a, b) {
244
+ var measure = this.userTiming().getEntriesByName(this._measureName(a, b))[0] || {};
245
+ return measure.duration;
246
+ };
247
+
248
+ SNPProto.duration = function(a, b, options) {
249
+ if(this._userTiming) {
250
+ this._setMeasure(a, b);
251
+ return this._round(this._getMeasureDuration(a, b), options);
252
+ } else if(this._highResTime && a === 'navigationStart' && !this._isTimingMark(b)) {
253
+ return this._round(this.getMark(b), options);
254
+ } else {
255
+ return this._roundedDuration(this._getDurationMark(a), this._getDurationMark(b), options);
256
+ }
257
+ };
258
+
259
+ SNPProto.updateEvent = function(eventKey, key, value) {
260
+ var obj = {};
261
+ obj[eventKey] = {};
262
+
263
+ defaults(this._data.events, obj);
264
+
265
+ this._data.events[eventKey][key] = value;
266
+ };
267
+
268
+ SNPProto.resetEvent = function(eventKey, key, value) {
269
+ this._data.events[eventKey] = {};
270
+ this._data.events[eventKey][key] = value;
271
+ };
272
+
273
+ SNPProto.eventStart = function(eventKey) {
274
+ this.resetEvent(eventKey, 'start', this.now());
275
+ };
276
+
277
+ SNPProto.eventEnd = function(eventKey, options) {
278
+ var now = this.now(),
279
+ key;
280
+
281
+ options = options || {};
282
+
283
+ for(key in options) {
284
+ this.updateEvent(eventKey, key, options[key]);
285
+ }
286
+
287
+ this.updateEvent(eventKey, 'end', now);
288
+ };
289
+
290
+ SNPProto.getEventData = function(eventKey, key) {
291
+ var eventObj = this._data.events[eventKey];
292
+ if(eventObj) {
293
+ return eventObj[key];
294
+ }
295
+ };
296
+
297
+ SNPProto.eventDuration = function(eventKey, options) {
298
+ return this._roundedDuration(this.getEventData(eventKey, 'start'), this.getEventData(eventKey, 'end'), options);
299
+ };
300
+
301
+ SNPProto.setCustom = function(key, value) {
302
+ this._data.custom[key] = value;
303
+ };
304
+
305
+ SNPProto.getCustom = function(key) {
306
+ return this._data.custom[key];
307
+ };
308
+
309
+ /**
310
+ * Total time for App Cache, DNS & TCP
311
+ *
312
+ * @returns {integer} time in ms
313
+ * @memberOf SurfNPerf
314
+ */
315
+ SNPProto.getNetworkTime = function() {
316
+ return this.duration('fetchStart', 'connectEnd');
317
+ };
318
+
319
+ /**
320
+ * Total time for Request & Response
321
+ *
322
+ * @returns {integer} time in ms
323
+ * @memberOf SurfNPerf
324
+ */
325
+ SNPProto.getServerTime = function() {
326
+ return this.duration('requestStart', 'responseEnd');
327
+ };
328
+
329
+ /**
330
+ * Total time for App Cache, DNS, TCP, Request & Response
331
+ *
332
+ * @returns {integer} time in ms
333
+ * @memberOf SurfNPerf
334
+ */
335
+ SNPProto.getNetworkLatency = function() {
336
+ return this.duration('fetchStart', 'responseEnd');
337
+ };
338
+
339
+ /**
340
+ * Total time to process the Response & fire the onLoad event
341
+ *
342
+ * @returns {integer} time in ms
343
+ * @memberOf SurfNPerf
344
+ */
345
+ SNPProto.getProcessingLoadTime = function() {
346
+ return this.duration('responseEnd', 'loadEventEnd');
347
+ };
348
+
349
+ /**
350
+ * Total time for a page to load from the time the user initiates the access of the page to the firing of the onLoad event
351
+ *
352
+ * @returns {integer} time in ms
353
+ * @memberOf SurfNPerf
354
+ */
355
+ SNPProto.getFullRequestLoadTime = function() {
356
+ return this.duration('navigationStart', 'loadEventEnd');
357
+ };
358
+
359
+ return new SurfNPerf();
360
+
361
+ }));
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: surfnperf
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ platform: ruby
6
+ authors:
7
+ - John Riviello
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Micro-library for gathering frontend web page performance data
42
+ email:
43
+ - john_riviello@comcast.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - README.md
49
+ - app/views/surfnperf/_head.html.erb
50
+ - lib/surfnperf.rb
51
+ - lib/surfnperf/rails.rb
52
+ - vendor/assets/javascripts/surfnperf.js
53
+ homepage: https://github.com/Comcast/Surf-N-Perf
54
+ licenses:
55
+ - MIT
56
+ metadata: {}
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 2.4.3
74
+ signing_key:
75
+ specification_version: 4
76
+ summary: Micro-library for gathering frontend web page performance data
77
+ test_files: []