miniprofiler 0.1.7.1
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.
- data/.gitignore +4 -0
- data/CHANGELOG +32 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +64 -0
- data/README.md +110 -0
- data/Rakefile +40 -0
- data/autotest/discover.rb +2 -0
- data/lib/mini_profiler/body_add_proxy.rb +45 -0
- data/lib/mini_profiler/client_timer_struct.rb +76 -0
- data/lib/mini_profiler/config.rb +52 -0
- data/lib/mini_profiler/context.rb +10 -0
- data/lib/mini_profiler/page_timer_struct.rb +53 -0
- data/lib/mini_profiler/profiler.rb +405 -0
- data/lib/mini_profiler/profiling_methods.rb +73 -0
- data/lib/mini_profiler/request_timer_struct.rb +96 -0
- data/lib/mini_profiler/sql_timer_struct.rb +48 -0
- data/lib/mini_profiler/storage/abstract_store.rb +27 -0
- data/lib/mini_profiler/storage/file_store.rb +108 -0
- data/lib/mini_profiler/storage/memory_store.rb +68 -0
- data/lib/mini_profiler/storage/redis_store.rb +44 -0
- data/lib/mini_profiler/timer_struct.rb +31 -0
- data/lib/mini_profiler_rails/railtie.rb +84 -0
- data/lib/patches/sql_patches.rb +185 -0
- data/lib/rack-mini-profiler.rb +6 -0
- data/rack-mini-profiler.gemspec +23 -0
- data/spec/components/body_add_proxy_spec.rb +90 -0
- data/spec/components/client_timer_struct_spec.rb +165 -0
- data/spec/components/file_store_spec.rb +47 -0
- data/spec/components/memory_store_spec.rb +40 -0
- data/spec/components/page_timer_struct_spec.rb +33 -0
- data/spec/components/profiler_spec.rb +126 -0
- data/spec/components/redis_store_spec.rb +44 -0
- data/spec/components/request_timer_struct_spec.rb +148 -0
- data/spec/components/sql_timer_struct_spec.rb +63 -0
- data/spec/components/timer_struct_spec.rb +47 -0
- data/spec/fixtures/simple_client_request.yml +17 -0
- data/spec/fixtures/weird_client_request.yml +13 -0
- data/spec/integration/mini_profiler_spec.rb +142 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/expand_each_to_matcher.rb +8 -0
- data/test_old/config.ru +41 -0
- metadata +155 -0
data/.gitignore
ADDED
data/CHANGELOG
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
28-June-2012 - Sam
|
2
|
+
|
3
|
+
* Started change log
|
4
|
+
* Corrected profiler so it properly captures POST requests (was supressing non 200s)
|
5
|
+
* Amended Rack.MiniProfiler.config[:user_provider] to use ip addres for identity
|
6
|
+
* Fixed bug where unviewed missing ids never got cleared
|
7
|
+
* Supress all '/assets/' in the rails tie (makes debugging easier)
|
8
|
+
* record_sql was mega buggy
|
9
|
+
|
10
|
+
9-July-2012 - Sam
|
11
|
+
|
12
|
+
* Cleaned up mechanism for profiling in production, all you need to do now
|
13
|
+
is call Rack::MiniProfiler.authorize_request to get profiling working in
|
14
|
+
production
|
15
|
+
* Added option to display full backtraces pp=full-backtrace
|
16
|
+
* Cleaned up railties, got rid of the post authorize callback
|
17
|
+
* Version 0.1.3
|
18
|
+
|
19
|
+
12-July-2012 - Sam
|
20
|
+
|
21
|
+
* Fixed incorrect profiling steps (was not indenting or measuring start time right
|
22
|
+
* Implemented native PG and MySql2 interceptors, this gives way more accurate times
|
23
|
+
* Refactored context so its a proper class and not a hash
|
24
|
+
* Added some more client probing built in to rails
|
25
|
+
* More tests
|
26
|
+
|
27
|
+
18-July-2012 - Sam
|
28
|
+
|
29
|
+
* Added First Paint time for chrome
|
30
|
+
* Bug fix to ensure non Rails installs have mini profiler
|
31
|
+
* Version 0.1.7
|
32
|
+
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
miniprofiler (0.1.7.1)
|
5
|
+
rack (>= 1.1.3)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
ZenTest (4.8.1)
|
11
|
+
activemodel (3.2.6)
|
12
|
+
activesupport (= 3.2.6)
|
13
|
+
builder (~> 3.0.0)
|
14
|
+
activerecord (3.2.6)
|
15
|
+
activemodel (= 3.2.6)
|
16
|
+
activesupport (= 3.2.6)
|
17
|
+
arel (~> 3.0.2)
|
18
|
+
tzinfo (~> 0.3.29)
|
19
|
+
activesupport (3.2.6)
|
20
|
+
i18n (~> 0.6)
|
21
|
+
multi_json (~> 1.0)
|
22
|
+
arel (3.0.2)
|
23
|
+
autotest (4.4.6)
|
24
|
+
ZenTest (>= 4.4.1)
|
25
|
+
builder (3.0.0)
|
26
|
+
commonjs (0.2.6)
|
27
|
+
diff-lcs (1.1.3)
|
28
|
+
i18n (0.6.0)
|
29
|
+
less (2.2.1)
|
30
|
+
commonjs (~> 0.2.6)
|
31
|
+
libv8 (3.3.10.4)
|
32
|
+
multi_json (1.3.6)
|
33
|
+
rack (1.4.1)
|
34
|
+
rack-test (0.6.1)
|
35
|
+
rack (>= 1.0)
|
36
|
+
rake (0.9.2.2)
|
37
|
+
redis (3.0.1)
|
38
|
+
rspec (2.11.0)
|
39
|
+
rspec-core (~> 2.11.0)
|
40
|
+
rspec-expectations (~> 2.11.0)
|
41
|
+
rspec-mocks (~> 2.11.0)
|
42
|
+
rspec-core (2.11.0)
|
43
|
+
rspec-expectations (2.11.1)
|
44
|
+
diff-lcs (~> 1.1.3)
|
45
|
+
rspec-mocks (2.11.1)
|
46
|
+
therubyracer (0.10.1)
|
47
|
+
libv8 (~> 3.3.10)
|
48
|
+
tzinfo (0.3.33)
|
49
|
+
|
50
|
+
PLATFORMS
|
51
|
+
ruby
|
52
|
+
|
53
|
+
DEPENDENCIES
|
54
|
+
ZenTest
|
55
|
+
activerecord (~> 3.0)
|
56
|
+
autotest
|
57
|
+
less
|
58
|
+
miniprofiler!
|
59
|
+
rack
|
60
|
+
rack-test
|
61
|
+
rake
|
62
|
+
redis
|
63
|
+
rspec
|
64
|
+
therubyracer
|
data/README.md
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# NOTE FOR THIS GEM FORK
|
2
|
+
|
3
|
+
This is a fork the Ruby folder of https://github.com/SamSaffron/MiniProfiler which includes some quick fixes to make the original rack-mini-profiler gem a little more usable. I will consolidate any useful changes here into pull requests for the original repo.
|
4
|
+
|
5
|
+
* Add `gem 'miniprofiler'` to your Gemfile (install of 'rack-mini-profiler')
|
6
|
+
* `miniprofiler`'s version uses `x.y.z.t` syntax in order to more easily sync with `rack-mini-profiler`'s `x.y.z` SemVer versioning. I will increase `t` whenever I make a patch
|
7
|
+
* This gem fork will work in staging, not just development and production
|
8
|
+
* Gem release date will be correct (as of now 'rack-mini-profiler' hardcodes the release date to be 04/02/2012, which is very confusing: https://rubygems.org/gems/rack-mini-profiler -- I will report to them)
|
9
|
+
* Allow turning off loading jquery if the app already uses jquery
|
10
|
+
* More to come
|
11
|
+
|
12
|
+
# Original README below:
|
13
|
+
|
14
|
+
# rack-mini-profiler
|
15
|
+
|
16
|
+
Middleware that displays speed badge for every html page. Designed to work both in production and in development.
|
17
|
+
|
18
|
+
## Using rack-mini-profiler in your app
|
19
|
+
|
20
|
+
Install/add to Gemfile
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
gem 'rack-mini-profiler'
|
24
|
+
```
|
25
|
+
Using Rails:
|
26
|
+
|
27
|
+
All you have to do is include the Gem and you're good to go in development.
|
28
|
+
|
29
|
+
rack-mini-profiler is designed with production profiling in mind. To enable that just run `Rack::MiniProfiler.authorize_request` once you know a request is allowed to profile.
|
30
|
+
|
31
|
+
For example:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
# A hook in your ApplicationController
|
35
|
+
def authorize
|
36
|
+
if current_user.is_admin?
|
37
|
+
Rack::MiniProfiler.authorize_request
|
38
|
+
end
|
39
|
+
end
|
40
|
+
````
|
41
|
+
|
42
|
+
|
43
|
+
Using Builder:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
require 'rack-mini-profiler'
|
47
|
+
builder = Rack::Builder.new do
|
48
|
+
use Rack::MiniProfiler
|
49
|
+
|
50
|
+
map('/') { run get }
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
Using Sinatra:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
require 'rack-mini-profiler'
|
58
|
+
class MyApp < Sinatra::Base
|
59
|
+
use Rack::MiniProfiler
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
## Storage
|
64
|
+
|
65
|
+
By default, rack-mini-profiler stores its results in a memory store:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
# our default
|
69
|
+
Rack::MiniProfiler.config.storage = Rack::MiniProfiler::MemoryStore
|
70
|
+
```
|
71
|
+
|
72
|
+
There are 2 other available storage engines, `RedisStore` and `FileStore`.
|
73
|
+
|
74
|
+
MemoryStore is stores results in a processes heap - something that does not work well in a multi process environment.
|
75
|
+
FileStore stores results in the file system - something that may not work well in a multi machine environment.
|
76
|
+
|
77
|
+
Additionally you may implement an AbstractStore for your own provider.
|
78
|
+
|
79
|
+
Rails hooks up a FileStore for all environments.
|
80
|
+
|
81
|
+
## Running the Specs
|
82
|
+
|
83
|
+
```
|
84
|
+
$ rake build
|
85
|
+
$ rake spec
|
86
|
+
```
|
87
|
+
|
88
|
+
Additionally you can also run `autotest` if you like.
|
89
|
+
|
90
|
+
## Configuration Options
|
91
|
+
|
92
|
+
You can set configuration options using the configuration accessor on Rack::MiniProfiler:
|
93
|
+
|
94
|
+
```
|
95
|
+
# Have Mini Profiler show up on the right
|
96
|
+
Rack::MiniProfiler.config.position = 'right'
|
97
|
+
```
|
98
|
+
|
99
|
+
In a Rails app, this can be done conveniently in an initializer such as config/initializers/mini_profiler.rb.
|
100
|
+
|
101
|
+
## Available Options
|
102
|
+
|
103
|
+
* pre_authorize_cb - A lambda callback you can set to determine whether or not mini_profiler should be visible on a given request. Default in a Rails environment is only on in development mode. If in a Rack app, the default is always on.
|
104
|
+
* position - Can either be 'right' or 'left'. Default is 'left'.
|
105
|
+
* skip_schema_queries - Whether or not you want to log the queries about the schema of your tables. Default is 'false', 'true' in rails development.
|
106
|
+
|
107
|
+
## Special query strings
|
108
|
+
|
109
|
+
If you include the query string `pp=help` at the end of your request you will see the various option you have. You can use these options to extend or contract the amount of diagnostics rack-mini-profiler gathers.
|
110
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Rakefile
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler'
|
4
|
+
Bundler.setup(:default, :test)
|
5
|
+
require "bundler/gem_tasks"
|
6
|
+
|
7
|
+
task :default => [:spec]
|
8
|
+
|
9
|
+
require 'rspec/core'
|
10
|
+
require 'rspec/core/rake_task'
|
11
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
12
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "builds a gem"
|
16
|
+
task :build => :compile_less do
|
17
|
+
`gem build rack-mini-profiler.gemspec 1>&2`
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "compile less"
|
21
|
+
task :compile_less => :copy_files do
|
22
|
+
`lessc lib/html/includes.less > lib/html/includes.css`
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
desc "copy files from other parts of the tree"
|
27
|
+
task :copy_files do
|
28
|
+
`rm -R -f lib/html && mkdir lib/html 1>&2`
|
29
|
+
path = File.expand_path('../StackExchange.Profiling/UI')
|
30
|
+
`ln -s #{path}/includes.less lib/html/includes.less`
|
31
|
+
`ln -s #{path}/includes.js lib/html/includes.js`
|
32
|
+
`ln -s #{path}/includes.tmpl lib/html/includes.tmpl`
|
33
|
+
`ln -s #{path}/jquery.1.7.1.js lib/html/jquery.1.7.1.js`
|
34
|
+
`ln -s #{path}/jquery.tmpl.js lib/html/jquery.tmpl.js`
|
35
|
+
`ln -s #{path}/list.css lib/html/list.css`
|
36
|
+
`ln -s #{path}/list.js lib/html/list.js`
|
37
|
+
`ln -s #{path}/list.tmpl lib/html/list.tmpl`
|
38
|
+
`ln -s #{path}/include.partial.html lib/html/profile_handler.js`
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
|
4
|
+
# This class acts as a proxy to the Body so that we can
|
5
|
+
# safely append to the end without knowing about the internals
|
6
|
+
# of the body class.
|
7
|
+
class BodyAddProxy
|
8
|
+
def initialize(body, additional_text)
|
9
|
+
@body = body
|
10
|
+
@additional_text = additional_text
|
11
|
+
end
|
12
|
+
|
13
|
+
def respond_to?(*args)
|
14
|
+
super or @body.respond_to?(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(*args, &block)
|
18
|
+
@body.__send__(*args, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
# In the case of to_str we don't want to use method_missing as it might avoid
|
22
|
+
# a call to each (such as in Rack::Test)
|
23
|
+
def to_str
|
24
|
+
result = ""
|
25
|
+
each {|token| result << token}
|
26
|
+
result
|
27
|
+
end
|
28
|
+
|
29
|
+
def each(&block)
|
30
|
+
|
31
|
+
# In ruby 1.9 we don't support String#each
|
32
|
+
if @body.is_a?(String)
|
33
|
+
yield @body
|
34
|
+
else
|
35
|
+
@body.each(&block)
|
36
|
+
end
|
37
|
+
|
38
|
+
yield @additional_text
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'mini_profiler/timer_struct'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class MiniProfiler
|
5
|
+
|
6
|
+
# This class holds the client timings
|
7
|
+
class ClientTimerStruct < TimerStruct
|
8
|
+
|
9
|
+
def self.init_instrumentation
|
10
|
+
"<script type=\"text/javascript\">mPt=function(){var t=[];return{t:t,probe:function(n){t.push({d:new Date(),n:n})}}}()</script>"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.instrument(name,orig)
|
14
|
+
probe = "<script>mPt.probe('#{name}')</script>"
|
15
|
+
wrapped = probe
|
16
|
+
wrapped << orig
|
17
|
+
wrapped << probe
|
18
|
+
wrapped
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def initialize(env={})
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def init_from_form_data(env, page_struct)
|
27
|
+
timings = []
|
28
|
+
clientTimes, clientPerf, baseTime = nil
|
29
|
+
form = env['rack.request.form_hash']
|
30
|
+
|
31
|
+
clientPerf = form['clientPerformance'] if form
|
32
|
+
clientTimes = clientPerf['timing'] if clientPerf
|
33
|
+
|
34
|
+
baseTime = clientTimes['navigationStart'].to_i if clientTimes
|
35
|
+
return unless clientTimes && baseTime
|
36
|
+
|
37
|
+
probes = form['clientProbes']
|
38
|
+
translated = {}
|
39
|
+
if probes && probes != "null"
|
40
|
+
probes.each do |id, val|
|
41
|
+
name = val["n"]
|
42
|
+
translated[name] ||= {}
|
43
|
+
if translated[name][:start]
|
44
|
+
translated[name][:finish] = val["d"]
|
45
|
+
else
|
46
|
+
translated[name][:start] = val["d"]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
translated.each do |name, data|
|
52
|
+
h = {"Name" => name, "Start" => data[:start].to_i - baseTime}
|
53
|
+
h["Duration"] = data[:finish].to_i - data[:start].to_i if data[:finish]
|
54
|
+
timings.push(h)
|
55
|
+
end
|
56
|
+
|
57
|
+
clientTimes.keys.find_all{|k| k =~ /Start$/ }.each do |k|
|
58
|
+
start = clientTimes[k].to_i - baseTime
|
59
|
+
finish = clientTimes[k.sub(/Start$/, "End")].to_i - baseTime
|
60
|
+
duration = 0
|
61
|
+
duration = finish - start if finish > start
|
62
|
+
name = k.sub(/Start$/, "").split(/(?=[A-Z])/).map{|s| s.capitalize}.join(' ')
|
63
|
+
timings.push({"Name" => name, "Start" => start, "Duration" => duration}) if start >= 0
|
64
|
+
end
|
65
|
+
|
66
|
+
clientTimes.keys.find_all{|k| !(k =~ /(End|Start)$/)}.each do |k|
|
67
|
+
timings.push("Name" => k, "Start" => clientTimes[k].to_i - baseTime, "Duration" => -1)
|
68
|
+
end
|
69
|
+
|
70
|
+
self['RedirectCount'] = env['rack.request.form_hash']['clientPerformance']['navigation']['redirectCount']
|
71
|
+
self['Timings'] = timings
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
class Config
|
4
|
+
|
5
|
+
def self.attr_accessor(*vars)
|
6
|
+
@attributes ||= []
|
7
|
+
@attributes.concat vars
|
8
|
+
super(*vars)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.attributes
|
12
|
+
@attributes
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_accessor :auto_inject, :base_url_path, :pre_authorize_cb, :position,
|
16
|
+
:backtrace_remove, :backtrace_filter, :skip_schema_queries,
|
17
|
+
:storage, :user_provider, :storage_instance, :storage_options, :skip_paths, :authorization_mode, :use_existing_jquery
|
18
|
+
|
19
|
+
def self.default
|
20
|
+
new.instance_eval {
|
21
|
+
@auto_inject = true # automatically inject on every html page
|
22
|
+
@base_url_path = "/mini-profiler-resources/"
|
23
|
+
|
24
|
+
# called prior to rack chain, to ensure we are allowed to profile
|
25
|
+
@pre_authorize_cb = lambda {|env| true}
|
26
|
+
|
27
|
+
# called after rack chain, to ensure we are REALLY allowed to profile
|
28
|
+
@position = 'left' # Where it is displayed
|
29
|
+
@skip_schema_queries = false
|
30
|
+
@storage = MiniProfiler::MemoryStore
|
31
|
+
@user_provider = Proc.new{|env| Rack::Request.new(env).ip}
|
32
|
+
@authorization_mode = :allow_all
|
33
|
+
@use_existing_jquery = false
|
34
|
+
self
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def merge!(config)
|
39
|
+
return unless config
|
40
|
+
if Hash === config
|
41
|
+
config.each{|k,v| instance_variable_set "@#{k}",v}
|
42
|
+
else
|
43
|
+
self.class.attributes.each{ |k|
|
44
|
+
v = config.send k
|
45
|
+
instance_variable_set "@#{k}", v if v
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'mini_profiler/timer_struct'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class MiniProfiler
|
5
|
+
|
6
|
+
# PageTimerStruct
|
7
|
+
# Root: RequestTimer
|
8
|
+
# :has_many RequestTimer children
|
9
|
+
# :has_many SqlTimer children
|
10
|
+
class PageTimerStruct < TimerStruct
|
11
|
+
def initialize(env)
|
12
|
+
super("Id" => MiniProfiler.generate_id,
|
13
|
+
"Name" => env['PATH_INFO'],
|
14
|
+
"Started" => (Time.now.to_f * 1000).to_i,
|
15
|
+
"MachineName" => env['SERVER_NAME'],
|
16
|
+
"Level" => 0,
|
17
|
+
"User" => "unknown user",
|
18
|
+
"HasUserViewed" => false,
|
19
|
+
"ClientTimings" => ClientTimerStruct.new,
|
20
|
+
"DurationMilliseconds" => 0,
|
21
|
+
"HasTrivialTimings" => true,
|
22
|
+
"HasAllTrivialTimigs" => false,
|
23
|
+
"TrivialDurationThresholdMilliseconds" => 2,
|
24
|
+
"Head" => nil,
|
25
|
+
"DurationMillisecondsInSql" => 0,
|
26
|
+
"HasSqlTimings" => true,
|
27
|
+
"HasDuplicateSqlTimings" => false,
|
28
|
+
"ExecutedReaders" => 0,
|
29
|
+
"ExecutedScalars" => 0,
|
30
|
+
"ExecutedNonQueries" => 0)
|
31
|
+
name = "#{env['REQUEST_METHOD']} http://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{env['SCRIPT_NAME']}#{env['PATH_INFO']}"
|
32
|
+
self['Root'] = RequestTimerStruct.createRoot(name, self)
|
33
|
+
end
|
34
|
+
|
35
|
+
def duration_ms
|
36
|
+
@attributes['Root']['DurationMilliseconds']
|
37
|
+
end
|
38
|
+
|
39
|
+
def root
|
40
|
+
@attributes['Root']
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_json(*a)
|
44
|
+
attribs = @attributes.merge(
|
45
|
+
"Started" => '/Date(%d)/' % @attributes['Started'],
|
46
|
+
"DurationMilliseconds" => @attributes['Root']['DurationMilliseconds']
|
47
|
+
)
|
48
|
+
::JSON.generate(attribs, :max_nesting => 100)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|