gitlab-peek 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.travis.yml +12 -0
  4. data/CHANGELOG.md +96 -0
  5. data/Gemfile +30 -0
  6. data/Gemfile-rails42 +27 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +253 -0
  9. data/Rakefile +13 -0
  10. data/app/assets/javascripts/peek.js +86 -0
  11. data/app/assets/javascripts/peek/vendor/jquery.tipsy.js +258 -0
  12. data/app/assets/stylesheets/peek.scss +85 -0
  13. data/app/assets/stylesheets/peek/vendor/tipsy.scss +22 -0
  14. data/app/controllers/peek/results_controller.rb +25 -0
  15. data/app/views/peek/_bar.html.erb +11 -0
  16. data/config/routes.rb +3 -0
  17. data/gitlab-peek.gemspec +22 -0
  18. data/lib/peek.rb +93 -0
  19. data/lib/peek/adapters/base.rb +17 -0
  20. data/lib/peek/adapters/elasticsearch.rb +33 -0
  21. data/lib/peek/adapters/memcache.rb +25 -0
  22. data/lib/peek/adapters/memory.rb +25 -0
  23. data/lib/peek/adapters/redis.rb +21 -0
  24. data/lib/peek/controller_helpers.rb +22 -0
  25. data/lib/peek/railtie.rb +37 -0
  26. data/lib/peek/version.rb +3 -0
  27. data/lib/peek/views/view.rb +124 -0
  28. data/test/controllers/requests_test.rb +33 -0
  29. data/test/dummy/README.rdoc +28 -0
  30. data/test/dummy/Rakefile +6 -0
  31. data/test/dummy/app/assets/javascripts/application.js +16 -0
  32. data/test/dummy/app/assets/stylesheets/application.css +14 -0
  33. data/test/dummy/app/controllers/application_controller.rb +13 -0
  34. data/test/dummy/app/controllers/home_controller.rb +11 -0
  35. data/test/dummy/app/helpers/application_helper.rb +2 -0
  36. data/test/dummy/app/views/home/show.html.erb +5 -0
  37. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  38. data/test/dummy/app/views/peek/_test_view.html.erb +1 -0
  39. data/test/dummy/bin/bundle +3 -0
  40. data/test/dummy/bin/rails +4 -0
  41. data/test/dummy/bin/rake +4 -0
  42. data/test/dummy/config.ru +4 -0
  43. data/test/dummy/config/application.rb +25 -0
  44. data/test/dummy/config/boot.rb +5 -0
  45. data/test/dummy/config/environment.rb +5 -0
  46. data/test/dummy/config/environments/development.rb +23 -0
  47. data/test/dummy/config/environments/test.rb +36 -0
  48. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  49. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  50. data/test/dummy/config/initializers/inflections.rb +16 -0
  51. data/test/dummy/config/initializers/mime_types.rb +5 -0
  52. data/test/dummy/config/initializers/peek.rb +3 -0
  53. data/test/dummy/config/initializers/secret_token.rb +12 -0
  54. data/test/dummy/config/initializers/session_store.rb +3 -0
  55. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  56. data/test/dummy/config/locales/en.yml +23 -0
  57. data/test/dummy/config/routes.rb +8 -0
  58. data/test/dummy/lib/test_view.rb +18 -0
  59. data/test/dummy/public/404.html +58 -0
  60. data/test/dummy/public/422.html +58 -0
  61. data/test/dummy/public/500.html +57 -0
  62. data/test/dummy/public/favicon.ico +0 -0
  63. data/test/peek/views/view_test.rb +37 -0
  64. data/test/peek_test.rb +69 -0
  65. data/test/test_helper.rb +22 -0
  66. 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 %>
@@ -0,0 +1,3 @@
1
+ Peek::Railtie.routes.draw do
2
+ get '/results' => 'results#show', as: :results
3
+ end
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Peek
2
+ VERSION = '0.0.1'
3
+ end
@@ -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