gitlab-peek 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.travis.yml +12 -0
- data/CHANGELOG.md +96 -0
- data/Gemfile +30 -0
- data/Gemfile-rails42 +27 -0
- data/LICENSE.txt +22 -0
- data/README.md +253 -0
- data/Rakefile +13 -0
- data/app/assets/javascripts/peek.js +86 -0
- data/app/assets/javascripts/peek/vendor/jquery.tipsy.js +258 -0
- data/app/assets/stylesheets/peek.scss +85 -0
- data/app/assets/stylesheets/peek/vendor/tipsy.scss +22 -0
- data/app/controllers/peek/results_controller.rb +25 -0
- data/app/views/peek/_bar.html.erb +11 -0
- data/config/routes.rb +3 -0
- data/gitlab-peek.gemspec +22 -0
- data/lib/peek.rb +93 -0
- data/lib/peek/adapters/base.rb +17 -0
- data/lib/peek/adapters/elasticsearch.rb +33 -0
- data/lib/peek/adapters/memcache.rb +25 -0
- data/lib/peek/adapters/memory.rb +25 -0
- data/lib/peek/adapters/redis.rb +21 -0
- data/lib/peek/controller_helpers.rb +22 -0
- data/lib/peek/railtie.rb +37 -0
- data/lib/peek/version.rb +3 -0
- data/lib/peek/views/view.rb +124 -0
- data/test/controllers/requests_test.rb +33 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +16 -0
- data/test/dummy/app/assets/stylesheets/application.css +14 -0
- data/test/dummy/app/controllers/application_controller.rb +13 -0
- data/test/dummy/app/controllers/home_controller.rb +11 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/home/show.html.erb +5 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/peek/_test_view.html.erb +1 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +25 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +23 -0
- data/test/dummy/config/environments/test.rb +36 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/peek.rb +3 -0
- data/test/dummy/config/initializers/secret_token.rb +12 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +8 -0
- data/test/dummy/lib/test_view.rb +18 -0
- data/test/dummy/public/404.html +58 -0
- data/test/dummy/public/422.html +58 -0
- data/test/dummy/public/500.html +57 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/peek/views/view_test.rb +37 -0
- data/test/peek_test.rb +69 -0
- data/test/test_helper.rb +22 -0
- metadata +161 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
module Peek
|
2
|
+
class ResultsController < ApplicationController
|
3
|
+
before_action :restrict_non_access
|
4
|
+
|
5
|
+
def show
|
6
|
+
respond_to do |format|
|
7
|
+
format.json do
|
8
|
+
if request.xhr?
|
9
|
+
render json: Peek.adapter.get(params[:request_id])
|
10
|
+
else
|
11
|
+
render nothing: true, status: :not_found
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def restrict_non_access
|
20
|
+
unless peek_enabled?
|
21
|
+
raise ActionController::RoutingError.new('Not Found')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<% if peek_enabled? %>
|
2
|
+
<div id="peek" class="<%= Peek.env %><%= ' disabled' if cookies[:peek] == 'false' %>" data-request-id="<%= peek_request_id %>">
|
3
|
+
<div class="wrapper">
|
4
|
+
<% Peek.views.each do |view| %>
|
5
|
+
<div id="<%= view.dom_id %>" class="view">
|
6
|
+
<%= render view.partial_path, view: view %>
|
7
|
+
</div>
|
8
|
+
<% end %>
|
9
|
+
</div>
|
10
|
+
</div>
|
11
|
+
<% end %>
|
data/config/routes.rb
ADDED
data/gitlab-peek.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'peek/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'gitlab-peek'
|
8
|
+
gem.version = Peek::VERSION
|
9
|
+
gem.authors = ['Garrett Bjerkhoel']
|
10
|
+
gem.email = ['me@garrettbjerkhoel.com']
|
11
|
+
gem.description = %q{Take a peek into your Rails application.}
|
12
|
+
gem.summary = %q{Take a peek into your Rails application.}
|
13
|
+
gem.homepage = 'https://github.com/peek/peek'
|
14
|
+
gem.license = 'MIT'
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
+
gem.require_paths = ['lib']
|
20
|
+
|
21
|
+
gem.add_dependency 'railties', '>= 4.0.0'
|
22
|
+
end
|
data/lib/peek.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'peek/version'
|
2
|
+
require 'rails'
|
3
|
+
|
4
|
+
require 'peek/adapters/memory'
|
5
|
+
require 'peek/views/view'
|
6
|
+
|
7
|
+
module Peek
|
8
|
+
ALLOWED_ENVS = ['development', 'staging'].freeze
|
9
|
+
|
10
|
+
def self.adapter
|
11
|
+
@adapter
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.adapter=(*adapter_options)
|
15
|
+
adapter, *parameters = *Array.wrap(adapter_options).flatten
|
16
|
+
|
17
|
+
@adapter = case adapter
|
18
|
+
when Symbol
|
19
|
+
adapter_class_name = adapter.to_s.camelize
|
20
|
+
adapter_class =
|
21
|
+
begin
|
22
|
+
require "peek/adapters/#{adapter}"
|
23
|
+
rescue LoadError => e
|
24
|
+
raise "Could not find adapter for #{adapter} (#{e})"
|
25
|
+
else
|
26
|
+
Peek::Adapters.const_get(adapter_class_name)
|
27
|
+
end
|
28
|
+
adapter_class.new(*parameters)
|
29
|
+
when nil
|
30
|
+
Peek::Adapters::Memory.new
|
31
|
+
else
|
32
|
+
adapter
|
33
|
+
end
|
34
|
+
|
35
|
+
@adapter
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.enabled?
|
39
|
+
ALLOWED_ENVS.include?(env)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.env
|
43
|
+
Rails.env
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.views
|
47
|
+
@cached_views ||= if @views && @views.any?
|
48
|
+
@views.collect { |klass, options| klass.new(options.dup) }.select(&:enabled?)
|
49
|
+
else
|
50
|
+
[]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.results
|
55
|
+
results = {
|
56
|
+
context: {},
|
57
|
+
data: Hash.new { |h, k| h[k] = {} }
|
58
|
+
}
|
59
|
+
|
60
|
+
views.each do |view|
|
61
|
+
if view.context?
|
62
|
+
results[:context][view.key] = view.context
|
63
|
+
end
|
64
|
+
|
65
|
+
view.results.each do |key, value|
|
66
|
+
results[:data][view.key][key] = value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
results
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.into(klass, options = {})
|
74
|
+
@views ||= []
|
75
|
+
@views << [klass, options]
|
76
|
+
end
|
77
|
+
|
78
|
+
# Clears out any and all views.
|
79
|
+
#
|
80
|
+
# Returns nothing.
|
81
|
+
def self.reset
|
82
|
+
@views = nil
|
83
|
+
@cached_views = nil
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.setup
|
87
|
+
ActiveSupport::Deprecation.warn "'Peek.setup' is deprecated and does nothing.", caller
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
require 'peek/railtie'
|
92
|
+
|
93
|
+
ActiveSupport.run_load_hooks(:peek, Peek)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Peek
|
2
|
+
module Adapters
|
3
|
+
class Base
|
4
|
+
def initialize(options = {})
|
5
|
+
|
6
|
+
end
|
7
|
+
|
8
|
+
def get(request_id)
|
9
|
+
raise "#{self.class}#get(request_id) is not yet implemented"
|
10
|
+
end
|
11
|
+
|
12
|
+
def save
|
13
|
+
raise "#{self.class}#save is not yet implemented"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'peek/adapters/base'
|
2
|
+
require 'elasticsearch'
|
3
|
+
|
4
|
+
module Peek
|
5
|
+
module Adapters
|
6
|
+
class Elasticsearch < Base
|
7
|
+
def initialize(options = {})
|
8
|
+
@client = options.fetch(:client, ::Elasticsearch::Client.new)
|
9
|
+
@expires_in = Integer(options.fetch(:expires_in, 60 * 30) * 1000)
|
10
|
+
@index = options.fetch(:index, 'peek_requests_index')
|
11
|
+
@type = options.fetch(:type, 'peek_request')
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(request_id)
|
15
|
+
result = @client.get_source index: @index, type: @type, id: "#{request_id}"
|
16
|
+
result.to_json
|
17
|
+
rescue ::Elasticsearch::Transport::Transport::Errors::NotFound
|
18
|
+
# pass
|
19
|
+
end
|
20
|
+
|
21
|
+
def save(request_id)
|
22
|
+
@client.index index: @index,
|
23
|
+
type: @type,
|
24
|
+
id: "#{request_id}",
|
25
|
+
body: Peek.results.to_json,
|
26
|
+
ttl: @expires_in
|
27
|
+
rescue ::Elasticsearch::Transport::Transport::Errors::BadRequest
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'peek/adapters/base'
|
2
|
+
require 'dalli'
|
3
|
+
|
4
|
+
module Peek
|
5
|
+
module Adapters
|
6
|
+
class Memcache < Base
|
7
|
+
def initialize(options = {})
|
8
|
+
@client = options.fetch(:client, ::Dalli::Client.new)
|
9
|
+
@expires_in = options.fetch(:expires_in, 60 * 30)
|
10
|
+
end
|
11
|
+
|
12
|
+
def get(request_id)
|
13
|
+
@client.get("peek:requests:#{request_id}")
|
14
|
+
rescue ::Dalli::DalliError => e
|
15
|
+
Rails.logger.error "#{e.class.name}: #{e.message}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def save(request_id)
|
19
|
+
@client.add("peek:requests:#{request_id}", Peek.results.to_json, @expires_in)
|
20
|
+
rescue ::Dalli::DalliError => e
|
21
|
+
Rails.logger.error "#{e.class.name}: #{e.message}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'peek/adapters/base'
|
2
|
+
|
3
|
+
module Peek
|
4
|
+
module Adapters
|
5
|
+
class Memory < Base
|
6
|
+
attr_accessor :requests
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
@requests = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def get(request_id)
|
13
|
+
@requests[request_id]
|
14
|
+
end
|
15
|
+
|
16
|
+
def save(request_id)
|
17
|
+
@requests[request_id] = Peek.results
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset
|
21
|
+
@requests.clear
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'peek/adapters/base'
|
2
|
+
require 'redis'
|
3
|
+
|
4
|
+
module Peek
|
5
|
+
module Adapters
|
6
|
+
class Redis < Base
|
7
|
+
def initialize(options = {})
|
8
|
+
@client = options.fetch(:client, ::Redis.new)
|
9
|
+
@expires_in = Integer(options.fetch(:expires_in, 60 * 30))
|
10
|
+
end
|
11
|
+
|
12
|
+
def get(request_id)
|
13
|
+
@client.get("peek:requests:#{request_id}")
|
14
|
+
end
|
15
|
+
|
16
|
+
def save(request_id)
|
17
|
+
@client.setex("peek:requests:#{request_id}", @expires_in, Peek.results.to_json)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Peek
|
2
|
+
module ControllerHelpers
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
if respond_to? :helper_method
|
7
|
+
helper_method :peek_enabled?
|
8
|
+
helper_method :peek_request_id
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def peek_enabled?
|
15
|
+
Peek.enabled?
|
16
|
+
end
|
17
|
+
|
18
|
+
def peek_request_id
|
19
|
+
request.env['action_dispatch.request_id']
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/peek/railtie.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'peek/controller_helpers'
|
2
|
+
|
3
|
+
module Peek
|
4
|
+
class Railtie < ::Rails::Engine
|
5
|
+
isolate_namespace Peek
|
6
|
+
engine_name :peek
|
7
|
+
|
8
|
+
config.peek = ActiveSupport::OrderedOptions.new
|
9
|
+
|
10
|
+
# Default adapter
|
11
|
+
config.peek.adapter = :memory
|
12
|
+
|
13
|
+
initializer 'peek.set_configs' do |app|
|
14
|
+
ActiveSupport.on_load(:peek) do
|
15
|
+
app.config.peek.each do |k,v|
|
16
|
+
send "#{k}=", v
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
initializer 'peek.persist_request_data' do
|
22
|
+
ActiveSupport::Notifications.subscribe('process_action.action_controller') do |_name, _start, _finish, _id, payload|
|
23
|
+
Peek.adapter.save(payload[:headers].env['action_dispatch.request_id'])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
initializer 'peek.include_controller_helpers' do
|
28
|
+
ActiveSupport.on_load(:action_controller) do
|
29
|
+
include Peek::ControllerHelpers
|
30
|
+
end
|
31
|
+
|
32
|
+
config.to_prepare do
|
33
|
+
Peek.views
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/peek/version.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
module Peek
|
2
|
+
module Views
|
3
|
+
class View
|
4
|
+
def initialize(options = {})
|
5
|
+
@options = options
|
6
|
+
|
7
|
+
parse_options
|
8
|
+
setup_subscribers
|
9
|
+
end
|
10
|
+
|
11
|
+
# Where any subclasses should pick and pull from @options to set any and
|
12
|
+
# all instance variables they like.
|
13
|
+
#
|
14
|
+
# Returns nothing.
|
15
|
+
def parse_options
|
16
|
+
# pass
|
17
|
+
end
|
18
|
+
|
19
|
+
# Conditionally enable views based on any gathered data. Helpful
|
20
|
+
# if you don't want views to show up when they return 0 or are
|
21
|
+
# touched during the request.
|
22
|
+
#
|
23
|
+
# Returns true.
|
24
|
+
def enabled?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
# The path to the partial that will be rendered to the Peek bar.
|
29
|
+
#
|
30
|
+
# Examples:
|
31
|
+
#
|
32
|
+
# Peek::Views::PerformanceBar.partial_path => "peek/views/performance_bar"
|
33
|
+
# CustomResque.partial_path => "performance_bar"
|
34
|
+
#
|
35
|
+
# Returns String.
|
36
|
+
def partial_path
|
37
|
+
self.class.to_s.underscore
|
38
|
+
end
|
39
|
+
|
40
|
+
# The defer key that is derived from the classname.
|
41
|
+
#
|
42
|
+
# Examples:
|
43
|
+
#
|
44
|
+
# Peek::Views::PerformanceBar => "performance-bar"
|
45
|
+
# Peek::Views::Resque => "resque"
|
46
|
+
#
|
47
|
+
# Returns String.
|
48
|
+
def key
|
49
|
+
self.class.to_s.split('::').last.underscore.gsub(/\_/, '-')
|
50
|
+
end
|
51
|
+
alias defer_key key
|
52
|
+
|
53
|
+
# The context id that is derived from the classname.
|
54
|
+
#
|
55
|
+
# Examples:
|
56
|
+
#
|
57
|
+
# Peek::Views::PerformanceBar => "peek-context-performance-bar"
|
58
|
+
# Peek::Views::Resque => "peek-context-resque"
|
59
|
+
#
|
60
|
+
# Returns String.
|
61
|
+
def context_id
|
62
|
+
"peek-context-#{key}"
|
63
|
+
end
|
64
|
+
|
65
|
+
# The wrapper ID for the individual view in the Peek bar.
|
66
|
+
#
|
67
|
+
# Returns String.
|
68
|
+
def dom_id
|
69
|
+
"peek-view-#{key}"
|
70
|
+
end
|
71
|
+
|
72
|
+
# Additional context for any view to render tooltips for.
|
73
|
+
#
|
74
|
+
# Returns Hash.
|
75
|
+
def context
|
76
|
+
{}
|
77
|
+
end
|
78
|
+
|
79
|
+
def context?
|
80
|
+
context.any?
|
81
|
+
end
|
82
|
+
|
83
|
+
# The data results that are inserted at the end of the request for use in
|
84
|
+
# deferred placeholders in the Peek the bar.
|
85
|
+
#
|
86
|
+
# Returns Hash.
|
87
|
+
def results
|
88
|
+
{}
|
89
|
+
end
|
90
|
+
|
91
|
+
def results?
|
92
|
+
results.any?
|
93
|
+
end
|
94
|
+
|
95
|
+
def subscribe(*args)
|
96
|
+
ActiveSupport::Notifications.subscribe(*args) do |name, start, finish, id, payload|
|
97
|
+
yield name, start, finish, id, payload
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def setup_subscribers
|
104
|
+
# pass
|
105
|
+
end
|
106
|
+
|
107
|
+
# Helper method for subscribing to the event that is fired when new
|
108
|
+
# requests are made.
|
109
|
+
def before_request
|
110
|
+
subscribe 'start_processing.action_controller' do |name, start, finish, id, payload|
|
111
|
+
yield name, start, finish, id, payload
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Helper method for subscribing to the event that is fired when requests
|
116
|
+
# are finished.
|
117
|
+
def after_request
|
118
|
+
subscribe 'process_action.action_controller' do |name, start, finish, id, payload|
|
119
|
+
yield name, start, finish, id, payload
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|