rack-allocation_stats 0.1.0

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.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/demo_rack_apps/Rails_3.2.15/app/controllers/application_controller.rb +3 -0
  3. data/demo_rack_apps/Rails_3.2.15/app/controllers/projects_controller.rb +83 -0
  4. data/demo_rack_apps/Rails_3.2.15/app/controllers/tasks_controller.rb +83 -0
  5. data/demo_rack_apps/Rails_3.2.15/app/helpers/application_helper.rb +2 -0
  6. data/demo_rack_apps/Rails_3.2.15/app/helpers/projects_helper.rb +2 -0
  7. data/demo_rack_apps/Rails_3.2.15/app/helpers/tasks_helper.rb +2 -0
  8. data/demo_rack_apps/Rails_3.2.15/app/models/project.rb +4 -0
  9. data/demo_rack_apps/Rails_3.2.15/app/models/task.rb +4 -0
  10. data/demo_rack_apps/Rails_3.2.15/config/application.rb +63 -0
  11. data/demo_rack_apps/Rails_3.2.15/config/boot.rb +6 -0
  12. data/demo_rack_apps/Rails_3.2.15/config/environment.rb +5 -0
  13. data/demo_rack_apps/Rails_3.2.15/config/environments/development.rb +37 -0
  14. data/demo_rack_apps/Rails_3.2.15/config/environments/production.rb +67 -0
  15. data/demo_rack_apps/Rails_3.2.15/config/environments/test.rb +37 -0
  16. data/demo_rack_apps/Rails_3.2.15/config/initializers/backtrace_silencers.rb +7 -0
  17. data/demo_rack_apps/Rails_3.2.15/config/initializers/inflections.rb +15 -0
  18. data/demo_rack_apps/Rails_3.2.15/config/initializers/mime_types.rb +5 -0
  19. data/demo_rack_apps/Rails_3.2.15/config/initializers/secret_token.rb +7 -0
  20. data/demo_rack_apps/Rails_3.2.15/config/initializers/session_store.rb +8 -0
  21. data/demo_rack_apps/Rails_3.2.15/config/initializers/wrap_parameters.rb +14 -0
  22. data/demo_rack_apps/Rails_3.2.15/config/routes.rb +64 -0
  23. data/demo_rack_apps/Rails_3.2.15/db/migrate/20131104031828_create_projects.rb +11 -0
  24. data/demo_rack_apps/Rails_3.2.15/db/migrate/20131104031829_create_tasks.rb +11 -0
  25. data/demo_rack_apps/Rails_3.2.15/db/schema.rb +32 -0
  26. data/demo_rack_apps/Rails_3.2.15/db/seeds.rb +15 -0
  27. data/demo_rack_apps/Rails_3.2.15/test/functional/projects_controller_test.rb +49 -0
  28. data/demo_rack_apps/Rails_3.2.15/test/functional/tasks_controller_test.rb +49 -0
  29. data/demo_rack_apps/Rails_3.2.15/test/performance/browsing_test.rb +12 -0
  30. data/demo_rack_apps/Rails_3.2.15/test/test_helper.rb +13 -0
  31. data/demo_rack_apps/Rails_3.2.15/test/unit/helpers/projects_helper_test.rb +4 -0
  32. data/demo_rack_apps/Rails_3.2.15/test/unit/helpers/tasks_helper_test.rb +4 -0
  33. data/demo_rack_apps/Rails_3.2.15/test/unit/project_test.rb +7 -0
  34. data/demo_rack_apps/Rails_3.2.15/test/unit/task_test.rb +7 -0
  35. data/demo_rack_apps/Rails_4.0.1/app/controllers/application_controller.rb +5 -0
  36. data/demo_rack_apps/Rails_4.0.1/app/controllers/projects_controller.rb +74 -0
  37. data/demo_rack_apps/Rails_4.0.1/app/controllers/tasks_controller.rb +74 -0
  38. data/demo_rack_apps/Rails_4.0.1/app/helpers/application_helper.rb +2 -0
  39. data/demo_rack_apps/Rails_4.0.1/app/helpers/projects_helper.rb +2 -0
  40. data/demo_rack_apps/Rails_4.0.1/app/helpers/tasks_helper.rb +2 -0
  41. data/demo_rack_apps/Rails_4.0.1/app/models/project.rb +3 -0
  42. data/demo_rack_apps/Rails_4.0.1/app/models/task.rb +3 -0
  43. data/demo_rack_apps/Rails_4.0.1/config/application.rb +24 -0
  44. data/demo_rack_apps/Rails_4.0.1/config/boot.rb +4 -0
  45. data/demo_rack_apps/Rails_4.0.1/config/environment.rb +5 -0
  46. data/demo_rack_apps/Rails_4.0.1/config/environments/development.rb +29 -0
  47. data/demo_rack_apps/Rails_4.0.1/config/environments/production.rb +80 -0
  48. data/demo_rack_apps/Rails_4.0.1/config/environments/test.rb +36 -0
  49. data/demo_rack_apps/Rails_4.0.1/config/initializers/backtrace_silencers.rb +7 -0
  50. data/demo_rack_apps/Rails_4.0.1/config/initializers/filter_parameter_logging.rb +4 -0
  51. data/demo_rack_apps/Rails_4.0.1/config/initializers/inflections.rb +16 -0
  52. data/demo_rack_apps/Rails_4.0.1/config/initializers/mime_types.rb +5 -0
  53. data/demo_rack_apps/Rails_4.0.1/config/initializers/secret_token.rb +12 -0
  54. data/demo_rack_apps/Rails_4.0.1/config/initializers/session_store.rb +3 -0
  55. data/demo_rack_apps/Rails_4.0.1/config/initializers/wrap_parameters.rb +14 -0
  56. data/demo_rack_apps/Rails_4.0.1/config/routes.rb +60 -0
  57. data/demo_rack_apps/Rails_4.0.1/db/migrate/20131102201320_create_projects.rb +11 -0
  58. data/demo_rack_apps/Rails_4.0.1/db/migrate/20131102201321_create_tasks.rb +11 -0
  59. data/demo_rack_apps/Rails_4.0.1/db/schema.rb +32 -0
  60. data/demo_rack_apps/Rails_4.0.1/db/seeds.rb +15 -0
  61. data/demo_rack_apps/Rails_4.0.1/test/controllers/projects_controller_test.rb +49 -0
  62. data/demo_rack_apps/Rails_4.0.1/test/controllers/tasks_controller_test.rb +49 -0
  63. data/demo_rack_apps/Rails_4.0.1/test/helpers/projects_helper_test.rb +4 -0
  64. data/demo_rack_apps/Rails_4.0.1/test/helpers/tasks_helper_test.rb +4 -0
  65. data/demo_rack_apps/Rails_4.0.1/test/models/project_test.rb +7 -0
  66. data/demo_rack_apps/Rails_4.0.1/test/models/task_test.rb +7 -0
  67. data/demo_rack_apps/Rails_4.0.1/test/test_helper.rb +15 -0
  68. data/lib/rack/allocation_stats.rb +22 -0
  69. data/lib/rack/allocation_stats/action.rb +14 -0
  70. data/lib/rack/allocation_stats/call_app_directly.rb +14 -0
  71. data/lib/rack/allocation_stats/formatters/base.rb +11 -0
  72. data/lib/rack/allocation_stats/formatters/html.rb +23 -0
  73. data/lib/rack/allocation_stats/formatters/json.rb +12 -0
  74. data/lib/rack/allocation_stats/formatters/text.rb +33 -0
  75. data/lib/rack/allocation_stats/middleware.rb +62 -0
  76. data/lib/rack/allocation_stats/tracer.rb +80 -0
  77. data/spec/factories.rb +53 -0
  78. data/spec/hello_world_app.rb +21 -0
  79. data/spec/rack/allocation_stats_spec.rb +216 -0
  80. data/spec/sinatra_app.rb +10 -0
  81. data/spec/sinatra_templates_app.rb +85 -0
  82. data/spec/spec_helper.rb +55 -0
  83. data/spec/yajl_app.rb +20 -0
  84. data/spec/yaml_app.rb +20 -0
  85. metadata +169 -0
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+
3
+ class TasksControllerTest < ActionController::TestCase
4
+ setup do
5
+ @task = tasks(:one)
6
+ end
7
+
8
+ test "should get index" do
9
+ get :index
10
+ assert_response :success
11
+ assert_not_nil assigns(:tasks)
12
+ end
13
+
14
+ test "should get new" do
15
+ get :new
16
+ assert_response :success
17
+ end
18
+
19
+ test "should create task" do
20
+ assert_difference('Task.count') do
21
+ post :create, task: { description: @task.description, name: @task.name, project_id: @task.project_id }
22
+ end
23
+
24
+ assert_redirected_to task_path(assigns(:task))
25
+ end
26
+
27
+ test "should show task" do
28
+ get :show, id: @task
29
+ assert_response :success
30
+ end
31
+
32
+ test "should get edit" do
33
+ get :edit, id: @task
34
+ assert_response :success
35
+ end
36
+
37
+ test "should update task" do
38
+ patch :update, id: @task, task: { description: @task.description, name: @task.name, project_id: @task.project_id }
39
+ assert_redirected_to task_path(assigns(:task))
40
+ end
41
+
42
+ test "should destroy task" do
43
+ assert_difference('Task.count', -1) do
44
+ delete :destroy, id: @task
45
+ end
46
+
47
+ assert_redirected_to tasks_path
48
+ end
49
+ end
@@ -0,0 +1,4 @@
1
+ require 'test_helper'
2
+
3
+ class ProjectsHelperTest < ActionView::TestCase
4
+ end
@@ -0,0 +1,4 @@
1
+ require 'test_helper'
2
+
3
+ class TasksHelperTest < ActionView::TestCase
4
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class ProjectTest < ActiveSupport::TestCase
4
+ # test "the truth" do
5
+ # assert true
6
+ # end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class TaskTest < ActiveSupport::TestCase
4
+ # test "the truth" do
5
+ # assert true
6
+ # end
7
+ end
@@ -0,0 +1,15 @@
1
+ ENV["RAILS_ENV"] ||= "test"
2
+ require File.expand_path('../../config/environment', __FILE__)
3
+ require 'rails/test_help'
4
+
5
+ class ActiveSupport::TestCase
6
+ ActiveRecord::Migration.check_pending!
7
+
8
+ # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
9
+ #
10
+ # Note: You'll currently still have to declare fixtures explicitly in integration tests
11
+ # -- they do not yet inherit this setting
12
+ fixtures :all
13
+
14
+ # Add more helper methods to be used by all tests here...
15
+ end
@@ -0,0 +1,22 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ require "bundler/setup"
5
+ require "rack"
6
+ require "allocation_stats"
7
+
8
+ require_relative "allocation_stats/action"
9
+ require_relative "allocation_stats/call_app_directly"
10
+ require_relative "allocation_stats/middleware"
11
+ require_relative "allocation_stats/tracer"
12
+
13
+ require_relative "allocation_stats/formatters/base"
14
+ require_relative "allocation_stats/formatters/html"
15
+ require_relative "allocation_stats/formatters/json"
16
+ require_relative "allocation_stats/formatters/text"
17
+
18
+ module Rack::AllocationStats
19
+ def self.new(app, options = {})
20
+ Middleware.new(app, options)
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ module Rack::AllocationStats
5
+ class Action
6
+ def initialize(env, middleware)
7
+ @env = env
8
+ @request = Rack::Request.new(env)
9
+ @get_params = @request.GET.clone
10
+ @middleware = middleware
11
+ @stats = nil
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ module Rack::AllocationStats
5
+ class CallAppDirectly < Action
6
+ def act
7
+ @result = @middleware.call_app(@env)
8
+ end
9
+
10
+ def response
11
+ @result
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ module Rack::AllocationStats::Formatters
5
+ class Base
6
+ def initialize(tracer, allocations)
7
+ @tracer = tracer
8
+ @allocations = allocations
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ class Rack::AllocationStats::Formatters::HTML < Rack::AllocationStats::Formatters::Base
5
+ def format
6
+ interactive_dir = File.join(__dir__, "..", "interactive")
7
+
8
+ index_html_erb = ERB.new(File.read(File.join(interactive_dir, "index.html.erb")))
9
+ jquery_min_js = File.read(File.join(interactive_dir, "jquery-2.0.3.min.js"))
10
+ jquery_ui_min_js = File.read(File.join(interactive_dir, "jquery-ui-1.10.3.custom.min.js"))
11
+ underscore_min_js = File.read(File.join(interactive_dir, "underscore-min.js"))
12
+ allocations_js_erb = ERB.new(File.read(File.join(interactive_dir, "allocations.js.erb")))
13
+ allocations_js = allocations_js_erb.result(binding)
14
+ interactive_js = File.read(File.join(interactive_dir, "interactive.js"))
15
+ jquery_ui_min_css = File.read(File.join(interactive_dir, "jquery-ui-1.10.3.custom.min.css"))
16
+ style_css = File.read(File.join(interactive_dir, "style.css"))
17
+
18
+ allocating_gems = ["no gems allocated any objects"]
19
+
20
+ html_body = index_html_erb.result(binding)
21
+ return [html_body]
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ class Rack::AllocationStats::Formatters::JSON < Rack::AllocationStats::Formatters::Base
5
+ def format
6
+ allocations = @allocations.to_a.map do |group_key, allocations|
7
+ { group_key: group_key, allocations: allocations }
8
+ end
9
+
10
+ return [Yajl::Encoder.encode(allocations)]
11
+ end
12
+ end
@@ -0,0 +1,33 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ class Rack::AllocationStats::Formatters::Text < Rack::AllocationStats::Formatters::Base
5
+ def format
6
+ file_length = @allocations.inject(0) { |length, allocation| [allocation[0][0].length , length].max }
7
+
8
+ total_count = @tracer.stats.allocations.all.size
9
+ bytes = @tracer.stats.allocations.bytes.all.inject { |sum, e| sum + e }
10
+ sums = "Total: #{total_count} allocations; #{bytes} bytes\n\n"
11
+
12
+ body = []
13
+
14
+ if @tracer.gc_report
15
+ gc_report = @tracer.stats.gc_profiler_report + "\n\n"
16
+ body += [gc_report]
17
+ end
18
+
19
+ running_total = 0
20
+ body + @allocations.map do |key, group|
21
+ sourcefile = key[0]
22
+ sourceline = key[1]
23
+ klass = key[2]
24
+ my_pct = (group.size * 1.0 / total_count) * 100
25
+ running_total += group.size
26
+ running_pct = (running_total * 1.0 / total_count) * 100
27
+
28
+ # TODO add a parameter to show/hide pct
29
+ #"%-#{file_length+4}s allocated %4d `%s` (%4.2f %4.2f)\n" % ["#{sourcefile}:#{sourceline}", group.size, klass, my_pct, running_pct]
30
+ "%-#{file_length+4}s allocated %4d `%s`\n" % ["#{sourcefile}:#{sourceline}", group.size, klass]
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,62 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ module Rack::AllocationStats
5
+ class Middleware
6
+ include Rack::Utils
7
+
8
+ def initialize(app, options = {})
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ @env = env
14
+ action = choose_action
15
+ action.act
16
+ action.response
17
+ end
18
+
19
+ def call_app(env)
20
+ @app.call(env)
21
+ end
22
+
23
+ def choose_action
24
+ request = Rack::Request.new(@env)
25
+ if request.GET["ras"] && request.GET["ras"].has_key?("help")
26
+ @content_type = "text/plain"
27
+ help_text
28
+ elsif request.GET["ras"] && request.GET["ras"]["trace"]
29
+ @content_type = content_type(request.GET["ras"]["output"])
30
+ Tracer.new(@env, self)
31
+ else
32
+ CallAppDirectly.new(@env, self)
33
+ end
34
+ end
35
+
36
+ def content_type(output_type)
37
+ case output_type
38
+ when "interactive" then "text/html"
39
+ when "json" then "application/json"
40
+ else "text/plain"
41
+ end
42
+ end
43
+
44
+ def allocation_stats_response(body)
45
+ [200, headers(body), Array(body)]
46
+ end
47
+
48
+ def headers(body)
49
+ {
50
+ "Content-Type" => @content_type,
51
+ "Content-Length" => body.inject(0) { |len, part| len + bytesize(part) }.to_s
52
+ }
53
+ end
54
+
55
+ def help_text
56
+ app = OpenStruct.new
57
+ app.body = [File.read(File.join(__dir__, "help.txt"))]
58
+ app.response = allocation_stats_response(app.body)
59
+ app
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,80 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ require 'yajl'
5
+
6
+ module Rack::AllocationStats
7
+ class Tracer < Action
8
+ include Rack::Utils
9
+
10
+ attr_reader :gc_report, :stats
11
+
12
+ def initialize(*args)
13
+ super
14
+ request = Rack::Request.new(@env)
15
+
16
+ # Once we're here, GET["ras"] better exist
17
+ @scope = request.GET["ras"]["scope"]
18
+ @times = (request.GET["ras"]["times"] || 1).to_i
19
+ @gc_report = request.GET["ras"]["gc_report"]
20
+ @output = (request.GET["ras"]["output"] || :columnar).to_sym
21
+ @alias_paths = request.GET["ras"]["alias_paths"] || false
22
+ @new_env = delete_custom_params(@env)
23
+ end
24
+
25
+ def act
26
+ @stats = AllocationStats.trace do
27
+ @times.times do
28
+ @middleware.call_app(@new_env)
29
+ end
30
+ end
31
+ end
32
+
33
+ def response
34
+ allocations = scoped_allocations
35
+
36
+ if @output == :interactive
37
+ allocations = allocations.all
38
+ @middleware.allocation_stats_response(Formatters::HTML.new(self, allocations).format)
39
+ elsif @output == :json
40
+ allocations = default_groups_and_sort(allocations)
41
+ @middleware.allocation_stats_response(Formatters::JSON.new(self, allocations).format)
42
+ else
43
+ allocations = default_groups_and_sort(allocations)
44
+ @middleware.allocation_stats_response(Formatters::Text.new(self, allocations).format)
45
+ end
46
+ end
47
+
48
+ def scoped_allocations
49
+ allocations = @stats.allocations(alias_paths: @alias_paths)
50
+
51
+ return allocations if @scope.nil?
52
+
53
+ if @scope == "."
54
+ return allocations.from_pwd
55
+ else
56
+ return allocations.from(@scope)
57
+ end
58
+ end
59
+
60
+ def default_groups_and_sort(allocations)
61
+ allocations.
62
+ group_by(:sourcefile, :sourceline, :class_plus).
63
+ sort_by_size.
64
+ all
65
+ end
66
+
67
+ def delete_custom_params(env)
68
+ new_env = env
69
+
70
+ get_params = Rack::Request.new(new_env).GET
71
+ get_params.delete("ras")
72
+
73
+ new_env.delete("rack.request.query_string")
74
+ new_env.delete("rack.request.query_hash")
75
+
76
+ new_env["QUERY_STRING"] = build_query(get_params)
77
+ new_env
78
+ end
79
+ end
80
+ end
data/spec/factories.rb ADDED
@@ -0,0 +1,53 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ FactoryGirl.define do
5
+ factory :allocation, class: AllocationStats::Allocation do
6
+
7
+ ignore do
8
+ object "A String"
9
+ sourcefile "foo/bar.rb"
10
+ sourceline 7
11
+ end
12
+
13
+ initialize_with { new(object) }
14
+
15
+ after(:build) do |allocation, evaluator|
16
+ allocation.instance_variable_set(:@sourcefile, evaluator.sourcefile)
17
+ allocation.instance_variable_set(:@sourceline, evaluator.sourceline)
18
+ end
19
+ end
20
+
21
+ # This might be overly complicated for now... but it was terribly fun to learn
22
+ # FactoryGirl's API, and I'll leave it for future use...
23
+ factory :stats, class: AllocationStats do
24
+ ignore do
25
+ size 5
26
+ files ["foo/bar.rb", "baz/quux.rb"]
27
+ sourceline 7
28
+ end
29
+
30
+ after(:build) do |stats, evaluator|
31
+ allocations = []
32
+ size = evaluator.size
33
+ files_count = evaluator.files.size
34
+
35
+ # 3 from 1st file
36
+ if size > 0
37
+ file = evaluator.files[0]
38
+ count = [size, 3].min
39
+ count.times { allocations << FactoryGirl.build(:allocation, sourcefile: file, sourceline: evaluator.sourceline) }
40
+ size -= count
41
+ end
42
+
43
+ # more from 2nd file
44
+ if size > 0
45
+ file = evaluator.files[1 % files_count]
46
+ size.times { allocations << FactoryGirl.build(:allocation, sourcefile: file, sourceline: evaluator.sourceline) }
47
+ size -= count
48
+ end
49
+
50
+ stats.instance_variable_set(:@new_allocations, allocations)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,21 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ require "rack"
5
+ require "yaml"
6
+
7
+ class HelloWorldApp
8
+ FULL_PATH = __FILE__
9
+
10
+ attr_accessor :allocating_lines
11
+
12
+ def initialize
13
+ @allocating_lines = []
14
+ end
15
+
16
+ def call(env)
17
+ a = "Ahhhhhhhhhhhhhhhhhhhhh"; @allocating_lines << __LINE__
18
+ #y = YAML.dump(["one string", "two string"]) # lots OF objects not from here
19
+ @allocating_lines << __LINE__; [200, {"Content-Type" => "text/html"}, ["Hello Rack!"]]
20
+ end
21
+ end