gitlab-peek 0.0.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.
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