├── test
├── dummy
│ ├── log
│ │ └── .keep
│ ├── public
│ │ ├── favicon.ico
│ │ ├── 500.html
│ │ ├── 422.html
│ │ └── 404.html
│ ├── app
│ │ ├── helpers
│ │ │ └── application_helper.rb
│ │ ├── views
│ │ │ ├── home
│ │ │ │ └── show.html.erb
│ │ │ └── layouts
│ │ │ │ └── application.html.erb
│ │ ├── controllers
│ │ │ ├── home_controller.rb
│ │ │ └── application_controller.rb
│ │ └── assets
│ │ │ ├── stylesheets
│ │ │ └── application.css
│ │ │ └── javascripts
│ │ │ └── application.js
│ ├── config
│ │ ├── routes.rb
│ │ ├── initializers
│ │ │ ├── session_store.rb
│ │ │ ├── filter_parameter_logging.rb
│ │ │ ├── mime_types.rb
│ │ │ ├── backtrace_silencers.rb
│ │ │ ├── wrap_parameters.rb
│ │ │ ├── secret_token.rb
│ │ │ └── inflections.rb
│ │ ├── environment.rb
│ │ ├── boot.rb
│ │ ├── locales
│ │ │ └── en.yml
│ │ ├── environments
│ │ │ ├── development.rb
│ │ │ └── test.rb
│ │ └── application.rb
│ ├── bin
│ │ ├── rake
│ │ ├── bundle
│ │ └── rails
│ ├── config.ru
│ ├── Rakefile
│ └── README.rdoc
├── controllers
│ └── requests_test.rb
├── test_helper.rb
├── peek
│ └── views
│ │ └── view_test.rb
└── peek_test.rb
├── lib
├── peek
│ ├── version.rb
│ ├── adapters
│ │ ├── base.rb
│ │ ├── memory.rb
│ │ ├── redis.rb
│ │ └── memcache.rb
│ ├── controller_helpers.rb
│ ├── railtie.rb
│ └── views
│ │ └── view.rb
└── peek.rb
├── config
└── routes.rb
├── app
├── assets
│ ├── images
│ │ └── peek
│ │ │ └── bar
│ │ │ ├── staging.gif
│ │ │ ├── development.gif
│ │ │ └── production.gif
│ ├── stylesheets
│ │ ├── peek.scss
│ │ └── peek
│ │ │ └── vendor
│ │ │ └── tipsy.scss
│ └── javascripts
│ │ ├── peek.coffee
│ │ └── peek
│ │ └── vendor
│ │ └── jquery.tipsy.js
├── views
│ └── peek
│ │ └── _bar.html.erb
└── controllers
│ └── peek
│ └── results_controller.rb
├── .gitignore
├── Rakefile
├── Gemfile
├── peek.gemspec
├── LICENSE.txt
├── CHANGELOG.md
└── README.md
/test/dummy/log/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/peek/version.rb:
--------------------------------------------------------------------------------
1 | module Peek
2 | VERSION = '0.1.8'
3 | end
4 |
--------------------------------------------------------------------------------
/test/dummy/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/test/dummy/config/routes.rb:
--------------------------------------------------------------------------------
1 | Dummy::Application.routes.draw do
2 | root :to => 'home#show'
3 | end
4 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | Peek::Railtie.routes.draw do
2 | get '/results' => 'results#show', :as => :results
3 | end
4 |
--------------------------------------------------------------------------------
/test/dummy/app/views/home/show.html.erb:
--------------------------------------------------------------------------------
1 |
Home#show
2 | Find me in app/views/home/show.html.erb
3 |
--------------------------------------------------------------------------------
/app/assets/images/peek/bar/staging.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/preact/peek/master/app/assets/images/peek/bar/staging.gif
--------------------------------------------------------------------------------
/test/dummy/app/controllers/home_controller.rb:
--------------------------------------------------------------------------------
1 | class HomeController < ApplicationController
2 | def show
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/test/dummy/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/app/assets/images/peek/bar/development.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/preact/peek/master/app/assets/images/peek/bar/development.gif
--------------------------------------------------------------------------------
/app/assets/images/peek/bar/production.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/preact/peek/master/app/assets/images/peek/bar/production.gif
--------------------------------------------------------------------------------
/test/dummy/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/test/dummy/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path('../../config/application', __FILE__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 |
--------------------------------------------------------------------------------
/test/dummy/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require ::File.expand_path('../config/environment', __FILE__)
4 | run Rails.application
5 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Dummy::Application.config.session_store :cookie_store, key: '_dummy_session'
4 |
--------------------------------------------------------------------------------
/test/dummy/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the rails application.
2 | require File.expand_path('../application', __FILE__)
3 |
4 | # Initialize the rails application.
5 | Dummy::Application.initialize!
6 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
5 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 | # Mime::Type.register_alias "text/html", :iphone
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | .bundle
4 | .config
5 | .yardoc
6 | Gemfile.lock
7 | InstalledFiles
8 | _yardoc
9 | coverage
10 | doc/
11 | lib/bundler/man
12 | pkg
13 | rdoc
14 | spec/reports
15 | test/dummy/log
16 | test/tmp
17 | test/version_tmp
18 | tmp
19 | /bin
20 |
--------------------------------------------------------------------------------
/test/dummy/config/boot.rb:
--------------------------------------------------------------------------------
1 | # Set up gems listed in the Gemfile.
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__)
3 |
4 | require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
5 | $LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__)
6 |
--------------------------------------------------------------------------------
/test/dummy/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require File.expand_path('../config/application', __FILE__)
5 |
6 | Dummy::Application.load_tasks
7 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'bundler/gem_tasks'
2 | require 'rake/testtask'
3 |
4 | desc 'Default: run tests'
5 | task :default => :test
6 |
7 | desc 'Run Peek tests.'
8 | Rake::TestTask.new do |t|
9 | t.libs << 'lib'
10 | t.libs << 'test'
11 | t.test_files = FileList['test/**/*_test.rb']
12 | t.verbose = true
13 | end
14 |
--------------------------------------------------------------------------------
/test/dummy/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | # Prevent CSRF attacks by raising an exception.
3 | # For APIs, you may want to use :null_session instead.
4 | protect_from_forgery with: :exception
5 |
6 | def peek_enabled?
7 | true
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/test/controllers/requests_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class RequestsTest < ActionDispatch::IntegrationTest
4 | setup do
5 | Peek.adapter.reset
6 | Peek.reset
7 | end
8 |
9 | test "the request id is set" do
10 | assert_empty Peek.adapter.requests
11 | get '/'
12 | assert_not_empty Peek.adapter.requests
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/peek/adapters/base.rb:
--------------------------------------------------------------------------------
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
18 |
--------------------------------------------------------------------------------
/test/dummy/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Dummy
5 | <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %>
6 | <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
7 | <%= csrf_meta_tags %>
8 |
9 |
10 | <%= render 'peek/bar' %>
11 | <%= yield %>
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/views/peek/_bar.html.erb:
--------------------------------------------------------------------------------
1 | <% if peek_enabled? %>
2 |
3 |
4 | <% Peek.views.each do |view| %>
5 |
6 | <%= render view.partial_path, :view => view %>
7 |
8 | <% end %>
9 |
10 |
11 | <% end %>
12 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/lib/peek/controller_helpers.rb:
--------------------------------------------------------------------------------
1 | module Peek
2 | module ControllerHelpers
3 | extend ActiveSupport::Concern
4 |
5 | included do
6 | prepend_before_filter :set_peek_request_id, :if => :peek_enabled?
7 | helper_method :peek_enabled?
8 | end
9 |
10 | protected
11 |
12 | def set_peek_request_id
13 | Peek.request_id = env['action_dispatch.request_id']
14 | end
15 |
16 | def peek_enabled?
17 | Peek.enabled?
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/peek/adapters/memory.rb:
--------------------------------------------------------------------------------
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
17 | @requests[Peek.request_id] = Peek.results
18 | end
19 |
20 | def reset
21 | @requests.clear
22 | end
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/controllers/peek/results_controller.rb:
--------------------------------------------------------------------------------
1 | module Peek
2 | class ResultsController < ApplicationController
3 | before_filter :restrict_non_access
4 | respond_to :json
5 |
6 | def show
7 | if request.xhr?
8 | render :json => Peek.adapter.get(params[:request_id])
9 | else
10 | render :nothing => true, :status => :not_found
11 | end
12 | end
13 |
14 | private
15 |
16 | def restrict_non_access
17 | unless peek_enabled?
18 | raise ActionController::RoutingError.new('Not Found')
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/lib/peek/adapters/redis.rb:
--------------------------------------------------------------------------------
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
17 | @client.setex("peek:requests:#{Peek.request_id}", @expires_in, Peek.results.to_json)
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/lib/peek/adapters/memcache.rb:
--------------------------------------------------------------------------------
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 | end
15 |
16 | def save
17 | @client.add("peek:requests:#{Peek.request_id}", Peek.results.to_json, @expires_in)
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/test/dummy/README.rdoc:
--------------------------------------------------------------------------------
1 | == README
2 |
3 | This README would normally document whatever steps are necessary to get the
4 | application up and running.
5 |
6 | Things you may want to cover:
7 |
8 | * Ruby version
9 |
10 | * System dependencies
11 |
12 | * Configuration
13 |
14 | * Database creation
15 |
16 | * Database initialization
17 |
18 | * How to run the test suite
19 |
20 | * Services (job queues, cache servers, search engines, etc.)
21 |
22 | * Deployment instructions
23 |
24 | * ...
25 |
26 |
27 | Please feel free to use a different markup language if you do not plan to run
28 | rake doc:app.
29 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | ENV['RAILS_ENV'] = 'test'
2 |
3 | require File.expand_path('../dummy/config/environment.rb', __FILE__)
4 | require 'rails/test_help'
5 |
6 | Rails.backtrace_cleaner.remove_silencers!
7 |
8 | # Load support files
9 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
10 |
11 | # Load fixtures from the engine
12 | if ActiveSupport::TestCase.method_defined?(:fixture_path=)
13 | ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
14 | end
15 |
16 | require 'minitest/autorun'
17 |
18 | begin
19 | require 'turn'
20 | rescue LoadError
21 | # Not installed.
22 | end
23 |
--------------------------------------------------------------------------------
/test/dummy/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the top of the
9 | * compiled file, but it's generally better to create a new file per style scope.
10 | *
11 | *= require peek
12 | *= require_self
13 | *= require_tree .
14 | */
15 |
--------------------------------------------------------------------------------
/test/dummy/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file.
9 | //
10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11 | // GO AFTER THE REQUIRES BELOW.
12 | //
13 | //= require peek
14 | //= require_tree .
15 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/secret_token.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Your secret key is used for verifying the integrity of signed cookies.
4 | # If you change this key, all old signed cookies will become invalid!
5 |
6 | # Make sure the secret is at least 30 characters and all random,
7 | # no regular words or you'll be exposed to dictionary attacks.
8 | # You can use `rake secret` to generate a secure secret key.
9 |
10 | # Make sure your secret_key_base is kept private
11 | # if you're sharing your code publicly.
12 | Rails.application.config.secret_key_base = '94c84623660ec36e05fa8584f7dad694c280aae1894eedb73fc017564933a4eea53962e7d8aba5dc33be928373045982ec9af92cef8150bb9576eaa55ad36d5b'
13 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/test/dummy/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # To learn more, please read the Rails Internationalization guide
20 | # available at http://guides.rubyonrails.org/i18n.html.
21 |
22 | en:
23 | hello: "Hello world"
24 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # Specify your gem's dependencies in peek.gemspec
4 | gemspec
5 |
6 | gem 'rake'
7 | gem 'json', '~> 1.7.7'
8 |
9 | gem 'rails', '~> 4.0.0.rc1'
10 |
11 | # Use SCSS for stylesheets
12 | gem 'sass-rails', '~> 4.0.0.rc1'
13 |
14 | # Use Uglifier as compressor for JavaScript assets
15 | gem 'uglifier', '>= 1.3.0'
16 |
17 | # Use CoffeeScript for .js.coffee assets and views
18 | gem 'coffee-rails', '~> 4.0.0'
19 |
20 | # See https://github.com/sstephenson/execjs#readme for more supported runtimes
21 | # gem 'therubyracer', platforms: :ruby
22 |
23 | # Use jquery as the JavaScript library
24 | gem 'jquery-rails'
25 |
26 | # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
27 | gem 'turbolinks'
28 |
--------------------------------------------------------------------------------
/test/peek/views/view_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | describe Peek::Views::View do
4 | before do
5 | @view = Peek::Views::View.new
6 | end
7 |
8 | describe "partial path" do
9 | it "should return correct partial class" do
10 | assert_equal 'peek/views/view', @view.partial_path
11 | end
12 | end
13 |
14 | describe "dom_id" do
15 | it "should return correct dom_id" do
16 | assert_equal 'peek-view-view', @view.dom_id
17 | end
18 | end
19 |
20 | describe "key" do
21 | it "should return correct key" do
22 | assert_equal 'view', @view.key
23 | end
24 | end
25 |
26 | describe "context" do
27 | it "should return correct context_id" do
28 | assert_equal 'peek-context-view', @view.context_id
29 | end
30 | end
31 |
32 | describe "toggling off and on" do
33 | it "should be enabled by default" do
34 | assert @view.enabled?
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/peek.gemspec:
--------------------------------------------------------------------------------
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 = '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 'rails', '>= 3.0.0'
22 | gem.add_dependency 'atomic', '>= 1.0.0'
23 | end
24 |
--------------------------------------------------------------------------------
/lib/peek/railtie.rb:
--------------------------------------------------------------------------------
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
23 | Peek.adapter.save
24 | Peek.clear
25 | end
26 | end
27 |
28 | initializer 'peek.include_controller_helpers' do
29 | ActiveSupport.on_load(:action_controller) do
30 | include Peek::ControllerHelpers
31 | end
32 |
33 | config.to_prepare do
34 | Peek.views
35 | end
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/test/dummy/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Dummy::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports and disable caching.
13 | config.consider_all_requests_local = true
14 | config.action_controller.perform_caching = false
15 |
16 | # Don't care if the mailer can't send.
17 | config.action_mailer.raise_delivery_errors = false
18 |
19 | # Debug mode disables concatenation and preprocessing of assets.
20 | # This option may cause significant delays in view rendering with a large
21 | # number of complex assets.
22 | config.assets.debug = true
23 | end
24 |
--------------------------------------------------------------------------------
/test/dummy/config/application.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../boot', __FILE__)
2 |
3 | require 'action_controller/railtie'
4 | require 'action_mailer/railtie'
5 | require 'sprockets/railtie'
6 | require 'rails/test_unit/railtie'
7 |
8 | Bundler.require(*Rails.groups)
9 | require 'peek'
10 |
11 | module Dummy
12 | class Application < Rails::Application
13 | # Settings in config/environments/* take precedence over those specified here.
14 | # Application configuration should go into files in config/initializers
15 | # -- all .rb files in that directory are automatically loaded.
16 |
17 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
18 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
19 | # config.time_zone = 'Central Time (US & Canada)'
20 |
21 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
22 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
23 | # config.i18n.default_locale = :de
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Garrett Bjerkhoel
2 |
3 | MIT License
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | "Software"), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/app/assets/stylesheets/peek.scss:
--------------------------------------------------------------------------------
1 | //= require peek/vendor/tipsy
2 |
3 | #peek {
4 | background: #000;
5 | height: 35px;
6 | line-height: 35px;
7 | color: #999;
8 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.75);
9 |
10 | .hidden {
11 | display: none;
12 | visibility: visible;
13 | }
14 |
15 | &.disabled {
16 | display: none;
17 | }
18 |
19 | &.production {
20 | background: image-url('peek/bar/production.gif') repeat 0 0;
21 | }
22 |
23 | &.staging {
24 | background: image-url('peek/bar/staging.gif') repeat 0 0;
25 | }
26 |
27 | &.development {
28 | background: image-url('peek/bar/development.gif') repeat 0 0;
29 | }
30 |
31 | .wrapper {
32 | width: 800px;
33 | margin: 0 auto;
34 | }
35 |
36 | // UI Elements
37 | .bucket {
38 | background: #111;
39 | display: inline-block;
40 | padding: 4px 6px;
41 | font-family: Consolas, "Liberation Mono", Courier, monospace;
42 | line-height: 1;
43 | color: #ccc;
44 | border-radius: 3px;
45 | box-shadow: 0 1px 0 rgba(255,255,255,.2), inset 0 1px 2px rgba(0,0,0,.25);
46 |
47 | .hidden {
48 | display: none;
49 | }
50 |
51 | &:hover .hidden {
52 | display: inline;
53 | }
54 | }
55 |
56 | strong {
57 | color: #fff;
58 | }
59 |
60 | .view {
61 | margin-right: 15px;
62 | float: left;
63 |
64 | &:last-child {
65 | margin-right: 0;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/test/dummy/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
48 |
49 |
50 |
51 |
52 |
53 |
We're sorry, but something went wrong.
54 |
55 | If you are the application owner check the logs for more information.
56 |
57 |
58 |
--------------------------------------------------------------------------------
/test/dummy/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
48 |
49 |
50 |
51 |
52 |
53 |
The change you wanted was rejected.
54 |
Maybe you tried to change something you didn't have access to.
55 |
56 | If you are the application owner check the logs for more information.
57 |
58 |
59 |
--------------------------------------------------------------------------------
/test/dummy/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
48 |
49 |
50 |
51 |
52 |
53 |
The page you were looking for doesn't exist.
54 |
You may have mistyped the address or the page may have moved.
55 |
56 | If you are the application owner check the logs for more information.
57 |
58 |
59 |
--------------------------------------------------------------------------------
/test/peek_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class Staff < Peek::Views::View
4 | def initialize(options = {})
5 | @username = options.delete(:username)
6 | end
7 |
8 | def username
9 | @username
10 | end
11 |
12 | def enabled?
13 | !!@username
14 | end
15 | end
16 |
17 | describe Peek do
18 | describe "enabled?" do
19 | it "should not be enabled in test" do
20 | refute Peek.enabled?
21 | end
22 | end
23 |
24 | describe "env" do
25 | it "should return the current environment" do
26 | assert_equal 'test', Peek.env
27 | end
28 | end
29 |
30 | describe "views" do
31 | before do
32 | Peek.reset
33 | end
34 |
35 | it "should have none by default" do
36 | assert_equal [], Peek.views
37 | end
38 |
39 | it "should be able to append views" do
40 | Peek.into Staff, :username => 'dewski'
41 | assert_kind_of Staff, Peek.views.first
42 | end
43 |
44 | it "should be able to append views with options" do
45 | Peek.into Staff, :username => 'dewski'
46 | @staff = Peek.views.first
47 | assert_kind_of Staff, @staff
48 | assert_equal 'dewski', @staff.username
49 | end
50 |
51 | it "should only return enabled views" do
52 | Peek.into Staff, :username => false
53 | assert_equal [], Peek.views
54 | end
55 | end
56 |
57 | describe "reset" do
58 | before do
59 | Peek.reset
60 | end
61 |
62 | it "should clear any current views" do
63 | Peek.into Staff, :username => 'dewski'
64 | assert_kind_of Staff, Peek.views.first
65 | Peek.reset
66 | assert_equal [], Peek.views
67 | end
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/test/dummy/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Dummy::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure static asset server for tests with Cache-Control for performance.
16 | config.serve_static_assets = true
17 | config.static_cache_control = "public, max-age=3600"
18 |
19 | # Show full error reports and disable caching.
20 | config.consider_all_requests_local = true
21 | config.action_controller.perform_caching = false
22 |
23 | # Raise exceptions instead of rendering exception templates.
24 | config.action_dispatch.show_exceptions = false
25 |
26 | # Disable request forgery protection in test environment.
27 | config.action_controller.allow_forgery_protection = false
28 |
29 | # Tell Action Mailer not to deliver emails to the real world.
30 | # The :test delivery method accumulates sent emails in the
31 | # ActionMailer::Base.deliveries array.
32 | config.action_mailer.delivery_method = :test
33 |
34 | # Print deprecation notices to the stderr.
35 | config.active_support.deprecation = :stderr
36 | end
37 |
--------------------------------------------------------------------------------
/app/assets/javascripts/peek.coffee:
--------------------------------------------------------------------------------
1 | #= require peek/vendor/jquery.tipsy
2 |
3 | requestId = null
4 |
5 | getRequestId = ->
6 | if requestId? then requestId else $('#peek').data('request-id')
7 |
8 | peekEnabled = ->
9 | $('#peek').length
10 |
11 | updatePerformanceBar = (results) ->
12 | for key of results.data
13 | for label of results.data[key]
14 | $("[data-defer-to=#{key}-#{label}]").text results.data[key][label]
15 | $(document).trigger 'peek:render', [getRequestId(), results]
16 |
17 | initializeTipsy = ->
18 | $('#peek .peek-tooltip, #peek .tooltip').each ->
19 | el = $(this)
20 | gravity = if el.hasClass('rightwards') || el.hasClass('leftwards')
21 | $.fn.tipsy.autoWE
22 | else
23 | $.fn.tipsy.autoNS
24 |
25 | el.tipsy
26 | gravity: gravity
27 |
28 | toggleBar = (event) ->
29 | return if $(event.target).is ':input'
30 |
31 | if event.which == 96 && !event.metaKey
32 | wrapper = $('#peek')
33 | if wrapper.hasClass 'disabled'
34 | wrapper.removeClass 'disabled'
35 | document.cookie = "peek=true; path=/";
36 | else
37 | wrapper.addClass 'disabled'
38 | document.cookie = "peek=false; path=/";
39 |
40 | fetchRequestResults = ->
41 | $.ajax '/peek/results',
42 | data:
43 | request_id: getRequestId()
44 | success: (data, textStatus, xhr) ->
45 | updatePerformanceBar data
46 | error: (xhr, textStatus, error) ->
47 | # Swallow the error
48 |
49 | $(document).on 'keypress', toggleBar
50 |
51 | $(document).on 'peek:update', initializeTipsy
52 | $(document).on 'peek:update', fetchRequestResults
53 |
54 | # Fire the event for our own listeners.
55 | $(document).on 'pjax:end', (event, xhr, options) ->
56 | if xhr?
57 | requestId = xhr.getResponseHeader 'X-Request-Id'
58 |
59 | if peekEnabled()
60 | $(this).trigger 'peek:update'
61 |
62 | # Also listen to turbolinks page change event
63 | $(document).on 'page:change', ->
64 | if peekEnabled()
65 | $(this).trigger 'peek:update'
66 |
67 | $ ->
68 | if peekEnabled()
69 | $(this).trigger 'peek:update'
70 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/peek/vendor/tipsy.scss:
--------------------------------------------------------------------------------
1 | .tipsy { font-size: 10px; position: absolute; padding: 5px; z-index: 100000; }
2 | .tipsy-inner { background-color: #000; color: #FFF; max-width: 200px; padding: 5px 8px 4px 8px; text-align: center; }
3 |
4 | /* Rounded corners */
5 | .tipsy-inner { border-radius: 3px; -moz-border-radius: 3px; -webkit-border-radius: 3px; }
6 |
7 | .tipsy-arrow { position: absolute; width: 0; height: 0; line-height: 0; border: 5px dashed #000; }
8 |
9 | /* Rules to colour arrows */
10 | .tipsy-arrow-n { border-bottom-color: #000; }
11 | .tipsy-arrow-s { border-top-color: #000; }
12 | .tipsy-arrow-e { border-left-color: #000; }
13 | .tipsy-arrow-w { border-right-color: #000; }
14 |
15 | .tipsy-n .tipsy-arrow { top: 0px; left: 50%; margin-left: -5px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent; }
16 | .tipsy-nw .tipsy-arrow { top: 0; left: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
17 | .tipsy-ne .tipsy-arrow { top: 0; right: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
18 | .tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
19 | .tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
20 | .tipsy-se .tipsy-arrow { bottom: 0; right: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
21 | .tipsy-e .tipsy-arrow { right: 0; top: 50%; margin-top: -5px; border-left-style: solid; border-right: none; border-top-color: transparent; border-bottom-color: transparent; }
22 | .tipsy-w .tipsy-arrow { left: 0; top: 50%; margin-top: -5px; border-right-style: solid; border-left: none; border-top-color: transparent; border-bottom-color: transparent; }
23 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 0.0.1
2 |
3 | - Initial release.
4 |
5 | # 0.0.2
6 |
7 | - Add own tipsy plugin to allow for tooltips.
8 |
9 | # 0.0.3
10 |
11 | - Change the scope of the .tipsy selector as it's inserted outside of the Glimpse div.
12 |
13 | # 0.0.4
14 |
15 | - Don't capture ` being pressed when in combination with `cmd`
16 | - Support for [Turbolinks](https://github.com/rails/turbolinks) (#14)
17 |
18 | # 0.0.5
19 |
20 | - Namespace the tooltips to the `.glimpse-tooltip` class name to not conflict with any application styles for `.tooltip`. (#18)
21 |
22 | # 0.0.6
23 |
24 | - Added Peek::Views::View#parse_options that gets called within initialize for subclasses to use to parse their options.
25 |
26 | # 0.1.0
27 |
28 | - Introduced a new JS event `peek:render` that includes the request id and request payload data that is used to update the information in the bar.
29 | - Request information has moved from the `peek/results` partial to an AJAX request that happens on page load, and when PJAX/Turbolinks change pages.
30 | - Removed the need for `peek/results` partial.
31 | - Introduced a Redis and Memcache adapter for multi-server environments to store request payloads.
32 | - Tooltips automatically repositions depending on where the Peek bar is.
33 |
34 | # 0.1.1
35 |
36 | - Fix bug with how `peek:render` was passing arguments around.
37 |
38 | # 0.1.2
39 |
40 | - Fix path to memcache adapter - [#34](https://github.com/peek/peek/pull/34) [@grk](https://github.com/grk)
41 | - Prevent namespace collision when using [peek-dalli](https://github.com/peek/peek-dalli) - [#34](https://github.com/peek/peek/pull/34) [@grk](https://github.com/grk)
42 |
43 | # 0.1.3
44 |
45 | - Remove Redis dependency from Gemfile
46 |
47 | # 0.1.4
48 |
49 | - Don't access xhr object when not present in pjax:end
50 |
51 | # 0.1.5
52 |
53 | - Don't trigger `peek:update` event when the peek bar isn't present - [#37](https://github.com/peek/peek/issues/37) [@dewski](https://github.com/dewski)
54 | - Add `after_request` helper method for Peek::Views::View to help reset state
55 |
56 | # 0.1.6
57 |
58 | - Use `event.which` for normalization between `event.keyCode` and `event.charCode` - [#38](https://github.com/peek/peek/pull/38) [@leongersing](https://github.com/leongersing)
59 |
60 | # 0.1.7
61 |
62 | - Support all Rails 3.x.x versions by not using `request.uuid` instead `env` - [#39](https://github.com/peek/peek/pull/39) [@bryanmikaelian](https://github.com/bryanmikaelian)
63 |
64 | # 0.1.8
65 |
66 | - Include the ControllerHelpers directly into `ActionController::Base` - [#41](https://github.com/peek/peek/pull/41) [@lucasmazza](https://github.com/lucasmazza)
67 |
--------------------------------------------------------------------------------
/lib/peek.rb:
--------------------------------------------------------------------------------
1 | require 'peek/version'
2 | require 'rails'
3 | require 'atomic'
4 |
5 | require 'peek/adapters/memory'
6 | require 'peek/views/view'
7 |
8 | module Peek
9 | def self._request_id
10 | @_request_id ||= Atomic.new
11 | end
12 |
13 | def self.request_id
14 | _request_id.get
15 | end
16 |
17 | def self.request_id=(id)
18 | _request_id.update { id }
19 | end
20 |
21 | def self.adapter
22 | @adapter
23 | end
24 |
25 | def self.adapter=(*adapter_options)
26 | adapter, *parameters = *Array.wrap(adapter_options).flatten
27 |
28 | @adapter = case adapter
29 | when Symbol
30 | adapter_class_name = adapter.to_s.camelize
31 | adapter_class =
32 | begin
33 | require "peek/adapters/#{adapter}"
34 | rescue LoadError => e
35 | raise "Could not find adapter for #{adapter} (#{e})"
36 | else
37 | Peek::Adapters.const_get(adapter_class_name)
38 | end
39 | adapter_class.new(*parameters)
40 | when nil
41 | Peek::Adapters::Memory.new
42 | else
43 | adapter
44 | end
45 |
46 | @adapter
47 | end
48 |
49 | def self.enabled?
50 | ['development', 'staging'].include?(env)
51 | end
52 |
53 | def self.env
54 | Rails.env
55 | end
56 |
57 | def self.views
58 | @cached_views ||= if @views && @views.any?
59 | @views.collect { |klass, options| klass.new(options.dup) }.select(&:enabled?)
60 | else
61 | []
62 | end
63 | end
64 |
65 | def self.results
66 | results = {
67 | :context => {},
68 | :data => Hash.new { |h, k| h[k] = {} }
69 | }
70 |
71 | views.each do |view|
72 | if view.context?
73 | results[:context][view.key] = view.context
74 | end
75 |
76 | view.results.each do |key, value|
77 | results[:data][view.key][key] = value
78 | end
79 | end
80 |
81 | results
82 | end
83 |
84 | def self.into(klass, options = {})
85 | @views ||= []
86 | @views << [klass, options]
87 | end
88 |
89 | # Clears out any and all views.
90 | #
91 | # Returns nothing.
92 | def self.reset
93 | @views = nil
94 | @cached_views = nil
95 | end
96 |
97 | # Hook that happens after every request. It is expected to reset
98 | # any state that Peek managed throughout the requests lifecycle.
99 | #
100 | # Returns nothing.
101 | def self.clear
102 | _request_id.update { '' }
103 | end
104 |
105 | def self.setup
106 | ActiveSupport::Deprecation.warn "'Peek.setup' is deprecated and does nothing.", caller
107 | end
108 | end
109 |
110 | require 'peek/railtie'
111 |
112 | ActiveSupport.run_load_hooks(:peek, Peek)
113 |
--------------------------------------------------------------------------------
/lib/peek/views/view.rb:
--------------------------------------------------------------------------------
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
125 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Peek
2 |
3 | [](https://travis-ci.org/peek/peek) [](http://badge.fury.io/rb/peek) [](http://inch-pages.github.io/github/peek/peek)
4 |
5 | Take a peek into your Rails application.
6 |
7 | 
8 |
9 | This was originally built at GitHub to help us get insight into what's going
10 | on, this is just an extraction so other Rails applications can have the same.
11 |
12 | ## Installation
13 |
14 | Add this line to your application's Gemfile:
15 |
16 | gem 'peek'
17 |
18 | And then execute:
19 |
20 | $ bundle
21 |
22 | Or install it yourself as:
23 |
24 | $ gem install peek
25 |
26 | ## Usage
27 |
28 | Now that Peek is installed, you'll need to mount the engine within your `config/routes.rb`
29 | file:
30 |
31 | ```ruby
32 | Some::Application.routes.draw do
33 | mount Peek::Railtie => '/peek'
34 | root :to => 'home#show'
35 | end
36 | ```
37 |
38 | To pick which views you want to see in your Peek bar, just create a file at
39 | `config/initializers/peek.rb` that has a list of the views you'd like to include:
40 |
41 | ```ruby
42 | Peek.into Peek::Views::Git, :nwo => 'github/janky'
43 | Peek.into Peek::Views::Mysql2
44 | Peek.into Peek::Views::Redis
45 | Peek.into Peek::Views::Dalli
46 | ```
47 |
48 | Feel free to pick and install from the [list](https://github.com/peek/peek#available-peek-views) or create your own. The order they
49 | are added to Peek, the order they will appear in your bar.
50 |
51 | Next, to render the Peek bar in your application just add the following snippet
52 | just after the opening `` tag in your application layout.
53 |
54 | ```erb
55 | <%= render 'peek/bar' %>
56 | ```
57 |
58 | It will look like:
59 |
60 | ```erb
61 |
62 |
63 | Application
64 |
65 |
66 | <%= render 'peek/bar' %>
67 | <%= yield %>
68 |
69 |
70 | ```
71 |
72 | Peek fetches the data collected throughout your requests by using the unique request id
73 | that was assigned to the request by Rails. It will call out to its own controller at
74 | [Peek::ResultsController](https://github.com/peek/peek/blob/master/app/controllers/peek/results_controller.rb) which will render the data and be inserted into the bar.
75 |
76 | Now that you have the partials in your application, you will need to include the
77 | CSS and JS that help make Peek :sparkles:
78 |
79 | In `app/assets/stylesheets/application.scss`:
80 |
81 | ```scss
82 | //= require peek
83 | ```
84 |
85 | In `app/assets/javascripts/application.coffee`:
86 |
87 | ```coffeescript
88 | #= require jquery
89 | #= require jquery_ujs
90 | #= require peek
91 | ```
92 |
93 | Note: Each additional view my have their own CSS and JS you need to require
94 | which should be stated in their usage documentation.
95 |
96 | ### Configuring the default adapter
97 |
98 | For Peek to work, it keeps track of all requests made in your application
99 | so it can report back and display that information in the Peek bar. By default
100 | it stores this information in memory, which is not recommended for production environments.
101 |
102 | In production environments you may have application servers on multiple hosts,
103 | at which Peek will not be able to access the request data if it was saved in memory on
104 | another host. Peek provides 2 additional adapters for multi server environments.
105 |
106 | You can configure which adapter Peek uses by updating your application
107 | config or an individual environment config file. We'll use production as an example.
108 |
109 | Note: Peek does not provide the dependencies for each of these adapters. If you use these
110 | adapters be sure to include their dependencies in your application.
111 |
112 | - Redis - The [redis](https://github.com/redis/redis-rb) gem
113 | - Dalli - The [dalli](https://github.com/mperham/dalli) gem
114 |
115 | ```ruby
116 | Peeked::Application.configure do
117 | # ...
118 |
119 | # Redis with no options
120 | config.peek.adapter = :redis
121 |
122 | # Redis with options
123 | config.peek.adapter = :redis, {
124 | :client => Redis.new,
125 | :expires_in => 60 * 30 # => 30 minutes in seconds
126 | }
127 |
128 | # Memcache with no options
129 | config.peek.adapter = :memcache
130 |
131 | # Memcache with options
132 | config.peek.adapter = :memcache, {
133 | :client => Dalli::Client.new,
134 | :expires_in => 60 * 30 # => 30 minutes in seconds
135 | }
136 |
137 | # ...
138 | end
139 | ```
140 |
141 | Peek doesn't persist the request data forever. It uses a safe 30 minute
142 | cache length that way data will be available if you'd like to aggregate it or
143 | use it for other Peek views. You can update this to be 30 seconds if you don't
144 | want the data to be available to stick around.
145 |
146 | ## Using Peek with PJAX
147 |
148 | It just works.
149 |
150 | ## Using Peek with Turbolinks
151 |
152 | It just works.
153 |
154 | ## Access Control
155 |
156 | Peek will only render in development and staging environments. If you'd
157 | like to whitelist a select number of users to view Peek in production you
158 | can override the `peek_enabled?` guard in `ApplicationController`:
159 |
160 | ```ruby
161 | class ApplicationController < ActionController::Base
162 | def peek_enabled?
163 | current_user.staff?
164 | end
165 | end
166 | ```
167 |
168 | ## Available Peek views
169 |
170 | - [peek-active_resource](https://github.com/gotmayonase/peek-active_resource)
171 | - [peek-dalli](https://github.com/peek/peek-dalli)
172 | - [peek-gc](https://github.com/peek/peek-gc)
173 | - [peek-git](https://github.com/peek/peek-git)
174 | - [peek-mongo](https://github.com/peek/peek-mongo)
175 | - [peek-moped](https://github.com/nodkz/peek-moped)
176 | - [peek-mysql2](https://github.com/peek/peek-mysql2)
177 | - [peek-performance_bar](https://github.com/peek/peek-performance_bar)
178 | - [peek-pg](https://github.com/peek/peek-pg)
179 | - [peek-rblineprof](https://github.com/peek/peek-rblineprof)
180 | - [peek-redis](https://github.com/peek/peek-redis)
181 | - [peek-resque](https://github.com/peek/peek-resque)
182 | - [peek-sidekiq](https://github.com/suranyami/peek-sidekiq)
183 | - [peek-faraday](https://github.com/grk/peek-faraday)
184 | - [peek-svn](https://github.com/neilco/peek-svn)
185 | - Unicorn :soon:
186 |
187 | Feel free to submit a Pull Request adding your own Peek item to this list.
188 |
189 | ## Creating your own Peek item
190 |
191 | Each Peek item is a self contained Rails engine which gives you the power to
192 | use all features of Ruby on Rails to dig in deep within your application and
193 | report it back to the Peek bar. A Peek item is just a custom class that
194 | is responsible for fetching and building the data that should be reported back
195 | to the user.
196 |
197 | There are still some docs to be written, but if you'd like to checkout a simple
198 | example of how to create your own, just checkout [peek-git](https://github.com/peek/peek-git).
199 | To just look at an example view, there is [Peek::Views::Git](https://github.com/peek/peek-git/blob/master/lib/peek/views/git.rb).
200 |
201 | ## Contributing
202 |
203 | 1. Fork it
204 | 2. Create your feature branch (`git checkout -b my-new-feature`)
205 | 3. Commit your changes (`git commit -am 'Add some feature'`)
206 | 4. Push to the branch (`git push origin my-new-feature`)
207 | 5. Create new Pull Request
208 |
--------------------------------------------------------------------------------
/app/assets/javascripts/peek/vendor/jquery.tipsy.js:
--------------------------------------------------------------------------------
1 | // tipsy, facebook style tooltips for jquery
2 | // version 1.0.0a
3 | // (c) 2008-2010 jason frame [jason@onehackoranother.com]
4 | // released under the MIT license
5 |
6 | (function($) {
7 |
8 | function maybeCall(thing, ctx) {
9 | return (typeof thing == 'function') ? (thing.call(ctx)) : thing;
10 | };
11 |
12 | function isElementInDOM(ele) {
13 | while (ele = ele.parentNode) {
14 | if (ele == document) return true;
15 | }
16 | return false;
17 | };
18 |
19 | function Tipsy(element, options) {
20 | this.$element = $(element);
21 | this.options = options;
22 | this.enabled = true;
23 | this.fixTitle();
24 | };
25 |
26 | Tipsy.prototype = {
27 | show: function() {
28 | var title = this.getTitle();
29 | if (title && this.enabled) {
30 | var $tip = this.tip();
31 |
32 | $tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title);
33 | $tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity
34 | $tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body);
35 |
36 | var pos = $.extend({}, this.$element.offset(), {
37 | width: this.$element[0].offsetWidth,
38 | height: this.$element[0].offsetHeight
39 | });
40 |
41 | var actualWidth = $tip[0].offsetWidth,
42 | actualHeight = $tip[0].offsetHeight,
43 | gravity = maybeCall(this.options.gravity, this.$element[0]);
44 |
45 | var tp;
46 | switch (gravity.charAt(0)) {
47 | case 'n':
48 | tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
49 | break;
50 | case 's':
51 | tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
52 | break;
53 | case 'e':
54 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset};
55 | break;
56 | case 'w':
57 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset};
58 | break;
59 | }
60 |
61 | if (gravity.length == 2) {
62 | if (gravity.charAt(1) == 'w') {
63 | tp.left = pos.left + pos.width / 2 - 15;
64 | } else {
65 | tp.left = pos.left + pos.width / 2 - actualWidth + 15;
66 | }
67 | }
68 |
69 | $tip.css(tp).addClass('tipsy-' + gravity);
70 | $tip.find('.tipsy-arrow')[0].className = 'tipsy-arrow tipsy-arrow-' + gravity.charAt(0);
71 | if (this.options.className) {
72 | $tip.addClass(maybeCall(this.options.className, this.$element[0]));
73 | }
74 |
75 | if (this.options.fade) {
76 | $tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity});
77 | } else {
78 | $tip.css({visibility: 'visible', opacity: this.options.opacity});
79 | }
80 | }
81 | },
82 |
83 | hide: function() {
84 | if (this.options.fade) {
85 | this.tip().stop().fadeOut(function() { $(this).remove(); });
86 | } else {
87 | this.tip().remove();
88 | }
89 | },
90 |
91 | fixTitle: function() {
92 | var $e = this.$element;
93 | if ($e.attr('title') || typeof($e.attr('original-title')) != 'string') {
94 | $e.attr('original-title', $e.attr('title') || '').removeAttr('title');
95 | }
96 | },
97 |
98 | getTitle: function() {
99 | var title, $e = this.$element, o = this.options;
100 | this.fixTitle();
101 | var title, o = this.options;
102 | if (typeof o.title == 'string') {
103 | title = $e.attr(o.title == 'title' ? 'original-title' : o.title);
104 | } else if (typeof o.title == 'function') {
105 | title = o.title.call($e[0]);
106 | }
107 | title = ('' + title).replace(/(^\s*|\s*$)/, "");
108 | return title || o.fallback;
109 | },
110 |
111 | tip: function() {
112 | if (!this.$tip) {
113 | this.$tip = $('').html('');
114 | this.$tip.data('tipsy-pointee', this.$element[0]);
115 | }
116 | return this.$tip;
117 | },
118 |
119 | validate: function() {
120 | if (!this.$element[0].parentNode) {
121 | this.hide();
122 | this.$element = null;
123 | this.options = null;
124 | }
125 | },
126 |
127 | enable: function() { this.enabled = true; },
128 | disable: function() { this.enabled = false; },
129 | toggleEnabled: function() { this.enabled = !this.enabled; }
130 | };
131 |
132 | $.fn.tipsy = function(options) {
133 |
134 | if (options === true) {
135 | return this.data('tipsy');
136 | } else if (typeof options == 'string') {
137 | var tipsy = this.data('tipsy');
138 | if (tipsy) tipsy[options]();
139 | return this;
140 | }
141 |
142 | options = $.extend({}, $.fn.tipsy.defaults, options);
143 |
144 | function get(ele) {
145 | var tipsy = $.data(ele, 'tipsy');
146 | if (!tipsy) {
147 | tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options));
148 | $.data(ele, 'tipsy', tipsy);
149 | }
150 | return tipsy;
151 | }
152 |
153 | function enter() {
154 | var tipsy = get(this);
155 | tipsy.hoverState = 'in';
156 | if (options.delayIn == 0) {
157 | tipsy.show();
158 | } else {
159 | tipsy.fixTitle();
160 | setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn);
161 | }
162 | };
163 |
164 | function leave() {
165 | var tipsy = get(this);
166 | tipsy.hoverState = 'out';
167 | if (options.delayOut == 0) {
168 | tipsy.hide();
169 | } else {
170 | setTimeout(function() { if (tipsy.hoverState == 'out') tipsy.hide(); }, options.delayOut);
171 | }
172 | };
173 |
174 | if (!options.live) this.each(function() { get(this); });
175 |
176 | if (options.trigger != 'manual') {
177 | var binder = options.live ? 'live' : 'bind',
178 | eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus',
179 | eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur';
180 | this[binder](eventIn, enter)[binder](eventOut, leave);
181 | }
182 |
183 | return this;
184 |
185 | };
186 |
187 | $.fn.tipsy.defaults = {
188 | className: null,
189 | delayIn: 0,
190 | delayOut: 0,
191 | fade: false,
192 | fallback: '',
193 | gravity: 'n',
194 | html: false,
195 | live: false,
196 | offset: 0,
197 | opacity: 0.8,
198 | title: 'title',
199 | trigger: 'hover'
200 | };
201 |
202 | $.fn.tipsy.revalidate = function() {
203 | $('.tipsy').each(function() {
204 | var pointee = $.data(this, 'tipsy-pointee');
205 | if (!pointee || !isElementInDOM(pointee)) {
206 | $(this).remove();
207 | }
208 | });
209 | };
210 |
211 | // Overwrite this method to provide options on a per-element basis.
212 | // For example, you could store the gravity in a 'tipsy-gravity' attribute:
213 | // return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' });
214 | // (remember - do not modify 'options' in place!)
215 | $.fn.tipsy.elementOptions = function(ele, options) {
216 | return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
217 | };
218 |
219 | $.fn.tipsy.autoNS = function() {
220 | return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
221 | };
222 |
223 | $.fn.tipsy.autoWE = function() {
224 | return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
225 | };
226 |
227 | /**
228 | * yields a closure of the supplied parameters, producing a function that takes
229 | * no arguments and is suitable for use as an autogravity function like so:
230 | *
231 | * @param margin (int) - distance from the viewable region edge that an
232 | * element should be before setting its tooltip's gravity to be away
233 | * from that edge.
234 | * @param prefer (string, e.g. 'n', 'sw', 'w') - the direction to prefer
235 | * if there are no viewable region edges effecting the tooltip's
236 | * gravity. It will try to vary from this minimally, for example,
237 | * if 'sw' is preferred and an element is near the right viewable
238 | * region edge, but not the top edge, it will set the gravity for
239 | * that element's tooltip to be 'se', preserving the southern
240 | * component.
241 | */
242 | $.fn.tipsy.autoBounds = function(margin, prefer) {
243 | return function() {
244 | var dir = {ns: prefer[0], ew: (prefer.length > 1 ? prefer[1] : false)},
245 | boundTop = $(document).scrollTop() + margin,
246 | boundLeft = $(document).scrollLeft() + margin,
247 | $this = $(this);
248 |
249 | if ($this.offset().top < boundTop) dir.ns = 'n';
250 | if ($this.offset().left < boundLeft) dir.ew = 'w';
251 | if ($(window).width() + $(document).scrollLeft() - $this.offset().left < margin) dir.ew = 'e';
252 | if ($(window).height() + $(document).scrollTop() - $this.offset().top < margin) dir.ns = 's';
253 |
254 | return dir.ns + (dir.ew ? dir.ew : '');
255 | }
256 | };
257 |
258 | })(jQuery);
259 |
--------------------------------------------------------------------------------