├── .codeclimate.yml ├── .github └── workflows │ └── tests.yml ├── .gitignore ├── .rubocop.yml ├── Appraisals ├── CHANGELOG.md ├── Gemfile ├── MIT-LICENSE ├── README.md ├── Rakefile ├── app ├── controllers │ └── tuttle │ │ ├── active_job_controller.rb │ │ ├── active_model_serializers_controller.rb │ │ ├── active_support_controller.rb │ │ ├── application_controller.rb │ │ ├── cancancan_controller.rb │ │ ├── devise_controller.rb │ │ ├── execjs_controller.rb │ │ ├── gems_controller.rb │ │ ├── home_controller.rb │ │ ├── i18n_controller.rb │ │ ├── rack_attack_controller.rb │ │ ├── rack_mini_profiler_controller.rb │ │ ├── rails_controller.rb │ │ ├── request_controller.rb │ │ └── ruby_controller.rb ├── helpers │ └── tuttle │ │ └── application_helper.rb ├── models │ └── tuttle │ │ ├── configuration_registry.rb │ │ └── version_detector.rb └── views │ ├── layouts │ └── tuttle │ │ └── application.html.erb │ └── tuttle │ ├── active_job │ └── index.html.erb │ ├── active_model_serializers │ ├── index.html.erb │ └── index9.html.erb │ ├── active_support │ ├── dependencies.html.erb │ ├── index.html.erb │ ├── inflectors.html.erb │ └── time_zones.html.erb │ ├── cancancan │ ├── _rule_table.html.erb │ ├── index.html.erb │ └── rule_tester.html.erb │ ├── devise │ └── index.html.erb │ ├── execjs │ └── index.html.erb │ ├── gems │ ├── get_process_mem.html.erb │ ├── http_clients.html.erb │ ├── index.html.erb │ ├── json.html.erb │ └── other.html.erb │ ├── home │ └── index.html.erb │ ├── i18n │ ├── _translation_entry.html.erb │ ├── index.html.erb │ ├── localize.html.erb │ └── translations.html.erb │ ├── rack_attack │ └── index.html.erb │ ├── rack_mini_profiler │ └── index.html.erb │ ├── rails │ ├── _cache_dalli_store.html.erb │ ├── _cache_memory_store.html.erb │ ├── _cache_monitor.html.erb │ ├── assets.html.erb │ ├── cache.html.erb │ ├── controllers.html.erb │ ├── database.html.erb │ ├── engines.html.erb │ ├── generators.html.erb │ ├── helpers.html.erb │ ├── index.html.erb │ ├── instrumentation.html.erb │ ├── models.html.erb │ ├── routes.html.erb │ └── schema_cache.html.erb │ ├── request │ └── index.html.erb │ └── ruby │ ├── constants.html.erb │ ├── extensions.html.erb │ ├── index.html.erb │ ├── miscellaneous.html.erb │ └── tuning.html.erb ├── config ├── rails_config_base.yml ├── rails_config_v6.x.yml └── routes.rb ├── gemfiles ├── rails_5.2.gemfile ├── rails_6.0.gemfile └── rails_6.1.gemfile ├── lib ├── tuttle.rb └── tuttle │ ├── engine.rb │ ├── instrumenter.rb │ ├── middleware │ └── request_profiler.rb │ ├── presenters │ ├── action_dispatch │ │ └── routing │ │ │ └── route_wrapper.rb │ ├── active_record │ │ └── reflection_presenter.rb │ ├── active_support │ │ └── callbacks.rb │ ├── base_presenter.rb │ └── rack_mini_profiler │ │ └── client_settings.rb │ ├── ruby_prof │ └── fast_call_stack_printer.rb │ └── version.rb ├── test ├── controllers │ └── tuttle │ │ ├── active_job_controller_test.rb │ │ ├── active_model_serializers_controller_test.rb │ │ ├── active_support_controller_test.rb │ │ ├── cancancan_controller_test.rb │ │ ├── devise_controller_test.rb │ │ ├── execjs_controller_test.rb │ │ ├── gems_controller_test.rb │ │ ├── home_controller_test.rb │ │ ├── i18n_controller_test.rb │ │ ├── rack_attack_controller_test.rb │ │ ├── rack_mini_profiler_controller_test.rb │ │ ├── rails_controller_test.rb │ │ ├── request_controller_test.rb │ │ └── ruby_controller_test.rb ├── dummy │ ├── README.rdoc │ ├── Rakefile │ ├── app │ │ ├── assets │ │ │ ├── config │ │ │ │ └── manifest.js │ │ │ ├── javascripts │ │ │ │ └── application.js │ │ │ └── stylesheets │ │ │ │ └── application.css │ │ ├── controllers │ │ │ └── application_controller.rb │ │ ├── helpers │ │ │ └── application_helper.rb │ │ ├── models │ │ │ ├── ability.rb │ │ │ ├── application_record.rb │ │ │ ├── note.rb │ │ │ └── user.rb │ │ └── views │ │ │ └── layouts │ │ │ └── application.html.erb │ ├── config.ru │ ├── config │ │ ├── application.rb │ │ ├── boot.rb │ │ ├── database.yml │ │ ├── environment.rb │ │ ├── environments │ │ │ ├── development.rb │ │ │ └── test.rb │ │ ├── initializers │ │ │ ├── backtrace_silencers.rb │ │ │ ├── devise.rb │ │ │ ├── inflections.rb │ │ │ ├── mime_types.rb │ │ │ ├── session_store.rb │ │ │ ├── tuttle.rb │ │ │ └── wrap_parameters.rb │ │ ├── locales │ │ │ └── en.yml │ │ ├── routes.rb │ │ └── secrets.yml │ ├── db │ │ ├── migrate │ │ │ ├── 20141229204528_devise_create_users.rb │ │ │ └── 20171223190950_create_notes.rb │ │ └── schema.rb │ ├── public │ │ ├── 404.html │ │ ├── 422.html │ │ ├── 500.html │ │ └── favicon.ico │ ├── script │ │ └── rails │ └── test │ │ ├── fixtures │ │ └── users.yml │ │ └── models │ │ └── user_test.rb ├── helpers │ └── application_helper_test.rb ├── integration │ └── profiling_middleware_test.rb ├── presenters │ └── active_record_reflection_presenter_test.rb ├── test_helper.rb └── tuttle_test.rb └── tuttle.gemspec /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | brakeman: 3 | enabled: true 4 | checks: 5 | CVE_2015_3227: 6 | enabled: false 7 | duplication: 8 | enabled: true 9 | config: 10 | languages: 11 | - ruby 12 | - javascript 13 | rubocop: 14 | enabled: true 15 | ratings: 16 | paths: 17 | - Gemfile.lock 18 | - "**.erb" 19 | - "**.haml" 20 | - "**.rb" 21 | - "**.js" 22 | - "**.jsx" 23 | exclude_paths: 24 | - config/ 25 | - coverage/ 26 | - gemfiles/ 27 | - script/ 28 | - test/ 29 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | name: Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }} 9 | strategy: 10 | matrix: 11 | ruby: ["3.0", "2.7"] 12 | rails: ["6.1", "6.0", "5.2"] 13 | exclude: 14 | - rails: "5.2" 15 | ruby: "3.0" 16 | env: 17 | BUNDLE_GEMFILE: gemfiles/rails_${{ matrix.rails }}.gemfile 18 | steps: 19 | - uses: actions/checkout@v2 20 | - uses: ruby/setup-ruby@v1 21 | with: 22 | ruby-version: ${{ matrix.ruby }} 23 | bundler-cache: true 24 | - name: Tests 25 | run: bundle exec rake app:db:setup test RAILS_ENV=test 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | .bundle/ 3 | gemfiles/*.lock 4 | log/*.log 5 | pkg/ 6 | rdoc/ 7 | coverage/ 8 | test/dummy/db/*.sqlite3 9 | test/dummy/log/*.log 10 | test/dummy/tmp/ 11 | test/dummy/.sass-cache 12 | 13 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: 2 | - rubocop-rails 3 | - rubocop-performance 4 | 5 | AllCops: 6 | TargetRubyVersion: 2.7 7 | DisplayCopNames: true 8 | DisplayStyleGuide: true 9 | NewCops: disable 10 | Exclude: 11 | - 'db/**/*' 12 | - 'config/**/*' 13 | - 'gemfiles/**/*' 14 | - 'script/**/*' 15 | - 'test/dummy/**/*' 16 | - 'lib/tuttle/ruby_prof/fast_call_stack_printer.rb' 17 | 18 | Style/Documentation: 19 | Enabled: false 20 | 21 | Style/FrozenStringLiteralComment: 22 | Enabled: false 23 | 24 | Layout/DotPosition: 25 | EnforcedStyle: trailing 26 | 27 | Layout/EmptyLineAfterMagicComment: 28 | Enabled: false 29 | 30 | Layout/EmptyLinesAroundClassBody: 31 | Enabled: false 32 | 33 | Layout/EmptyLinesAroundExceptionHandlingKeywords: 34 | Enabled: false 35 | 36 | Layout/EmptyLineBetweenDefs: 37 | Enabled: false 38 | 39 | Layout/MultilineMethodCallIndentation: 40 | Enabled: false 41 | 42 | Layout/SpaceInsideBlockBraces: 43 | Enabled: false 44 | SpaceBeforeBlockParameters: false 45 | EnforcedStyle: no_space 46 | 47 | Metrics/ClassLength: 48 | Max: 200 49 | 50 | Layout/LineLength: 51 | Enabled: false 52 | 53 | Metrics/MethodLength: 54 | Max: 30 55 | 56 | Metrics/AbcSize: 57 | Max: 40 58 | 59 | Security/MarshalLoad: 60 | Enabled: false 61 | 62 | Style/EmptyMethod: 63 | EnforcedStyle: expanded 64 | 65 | Style/GuardClause: 66 | Enabled: false 67 | 68 | Style/HashSyntax: 69 | Enabled: false 70 | 71 | Style/IfUnlessModifier: 72 | Enabled: false 73 | 74 | Style/SingleLineMethods: 75 | Enabled: false 76 | 77 | Style/StringLiterals: 78 | Enabled: false 79 | 80 | Style/RescueModifier: 81 | Enabled: false 82 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | appraise 'rails-6.1' do 2 | gem 'rails', '~> 6.1.3.2' 3 | end 4 | 5 | appraise 'rails-6.0' do 6 | gem 'rails', '~> 6.0.3.7' 7 | end 8 | 9 | appraise 'rails-5.2' do 10 | gem 'rails', '~> 5.2.6' 11 | end 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.1.0.alpha 2 | 3 | * Changes 4 | * Require Rails 5.2+ and Ruby 2.7+ 5 | * Drop paperclip support 6 | 7 | ### 0.0.8 8 | 9 | * Features 10 | * New ExecJS inspector 11 | * New Rack::Attack inspector 12 | * Improved ActiveRecord models inspector with associations and validations 13 | * Improved Rails Controllers link to matched routes (excluding wildcards and implicit renders) 14 | 15 | ### 0.0.7 16 | 17 | * Features 18 | * Rails 5.1 support (drops Rails 4.0) 19 | * New I18n inspector 20 | * New Constants and Object extensions inspector 21 | * Improved Rails routes inspector to show route validity 22 | * Improved listing display formatting 23 | 24 | ### 0.0.6 25 | 26 | * Features 27 | * Easier configuration with config.enable_profiling to load profiling middleware 28 | * Improved Asset Pipeline configuration inspector 29 | * Improved Rails Caching configuration inspector 30 | * Basic ActiveJob configuration inspector 31 | * Improved Rails general configuration options from Rails Guides 32 | * Syntax highlighting with highlight.js 33 | * Experimental inspector for Facter 34 | * Experimental profiler for Busted with additional DTrace inspections 35 | 36 | ### 0.0.5 37 | 38 | * Features 39 | * Request profiling middleware for ruby-prof and memory_profiler 40 | * ActiveSupport inspection including Dependencies, TimeZones, and Deprecation 41 | * Load path inspection (Ruby and Active Support autoloading) 42 | * No longer requires asset pipeline 43 | * Experimental inspector for Rack MiniProfiler and ActiveModelSerializers 44 | 45 | ### 0.0.4 46 | 47 | * Features 48 | * Experimental Rails5 support 49 | * Paperclip registry inspection 50 | * ActiveRecord schema cache and schema_cache.dump inspection 51 | * Configuration control of notification_tracking 52 | 53 | ### 0.0.3 54 | 55 | * Features 56 | * Rails caching instrumentation 57 | * Improved initialization/configuration approach 58 | * Gem detection and reporting for HTTP clients and JSON libraries 59 | * Ruby GC tuning stats and advice 60 | * Favicon! 61 | * Experimental Postgres stored-procedure cache inspection 62 | 63 | ### 0.0.2 64 | 65 | * Features 66 | * Tuttle::Engine will now auto-mount routes 67 | * Engine configurable via initializer 68 | * ActiveSupport cache configuration inspection 69 | * ActiveSupport inflectors inspection and simple testing 70 | 71 | ### 0.0.1 72 | 73 | * Features 74 | * Initial engine implementation with instrumentation monitoring 75 | * Rails inspection for general configuration, controllers, models, assets, helpers 76 | * Ruby VM inspection 77 | * Devise configuration inspection 78 | * CanCanCan configuration inspection and simple rule testing 79 | 80 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | group :development, :test do 6 | gem 'rails-controller-testing' 7 | 8 | # jquery-rails is used by the dummy application 9 | gem 'jquery-rails' 10 | 11 | gem 'sqlite3' 12 | 13 | gem 'active_model_serializers' 14 | gem 'busted' 15 | gem 'cancancan' 16 | gem 'devise', '>= 3.5.0' 17 | gem 'rack-attack', '>= 6.5.0' 18 | gem "redis", ">= 4.0" 19 | 20 | gem 'execjs' 21 | gem 'get_process_mem', ">= 0.2.7" 22 | gem 'memory_profiler' 23 | gem 'mini_racer' 24 | gem 'rack-mini-profiler', ">= 2.3.2" 25 | gem 'ruby-prof', ">= 1.4.3" 26 | 27 | gem 'codacy-coverage', require: false 28 | gem 'rubocop', require: false 29 | gem 'rubocop-rails', require: false 30 | gem "rubocop-performance", require: false 31 | end 32 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 YOURNAME 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Tuttle 3 | 4 | [![Build Status](https://github.com/dgynn/tuttle/workflows/tests/badge.svg)](https://github.com/dgynn/tuttle/actions) 5 | [![Code Climate](https://codeclimate.com/github/dgynn/tuttle/badges/gpa.svg)](https://codeclimate.com/github/dgynn/tuttle) 6 | 7 | Tuttle is a tool that helps Rails developers peek behind the curtain to inspect runtime configuration 8 | information for their applications. Tuttle can help with troubleshooting misconfigured 9 | apps or just help you better understand what is going on inside the frameworks you use. 10 | 11 | #### Features 12 | 13 | * Web dashboard mounted in your running app 14 | * Rails general configuration including defaults 15 | * Runtime internals of libraries and frameworks (eg ActiveRecord Query Cache) 16 | * Application inventory of Controllers and Models 17 | * Gems loaded 18 | * Ruby VM runtime information (GC stats, tuning parameters) 19 | * Optional profiling middleware for on-demand request profiling 20 | * Memory profiling using [memory_profiler](https://github.com/SamSaffron/memory_profiler) 21 | * CPU profiling using [ruby-prof](https://github.com/ruby-prof/ruby-prof) 22 | 23 | 24 | Tuttle has no dependencies other than Rails but works with a number of gems if 25 | they are loaded to provide inspections. 26 | Gems supported include devise, active_model_serializers, cancancan, and more. 27 | 28 | Tuttle is still in beta/proof-of-concept mode but is safe to use in development and disabled by default in other environments. 29 | 30 | You can see it in action in a [simple demo application](http://tuttle-demo.herokuapp.com/). The [source code](https://github.com/dgynn/tuttle-demo) for that demo is also on GitHub. 31 | 32 | ## To use... 33 | 34 | Add tuttle to your Gemfile 35 | ```ruby 36 | gem 'tuttle' 37 | 38 | # Include optional profiling gems 39 | gem 'memory_profiler' 40 | gem 'ruby-prof' 41 | 42 | ``` 43 | Browse to `/tuttle` 44 | 45 | ## Configuration 46 | 47 | Tuttle will automatically be enabled and mounted in development. To control the 48 | configuration, you can use an initializer. 49 | 50 | config/initializers/tuttle.rb 51 | ```ruby 52 | if defined?(::Tuttle::Engine) 53 | Tuttle.setup do |config| 54 | config.enabled = true # Defaults to true in development, false in other environments 55 | config.automount_engine = true # Defaults to true to mount the engine at /tuttle 56 | config.enable_profiling = true # Defaults to false 57 | end 58 | end 59 | ``` 60 | 61 | **Important:** Do not enable Tuttle in production. Tuttle does not require authentication 62 | and exposes internal application details. Sensitive data should be filtered but 63 | it is still not something end users should be able to access. 64 | 65 | It is also possible to use the profiling middleware without the Tuttle engine enabled. 66 | 67 | You can directly include the profiling middleware as follows: 68 | 69 | ```ruby 70 | # Add memory/cpu profiler middleware at the end of the stack 71 | require 'tuttle/middleware/request_profiler' 72 | Rails.application.config.middleware.use Tuttle::Middleware::RequestProfiler 73 | ``` 74 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/setup' 3 | rescue LoadError 4 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 5 | end 6 | 7 | APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__) 8 | load 'rails/tasks/engine.rake' 9 | 10 | Bundler::GemHelper.install_tasks 11 | 12 | require 'rake/testtask' 13 | 14 | Rake::TestTask.new(:test) do |t| 15 | t.libs << 'test' 16 | t.pattern = 'test/**/*_test.rb' 17 | t.verbose = false 18 | t.warning = false 19 | end 20 | 21 | task :default => :test 22 | -------------------------------------------------------------------------------- /app/controllers/tuttle/active_job_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_dependency 'tuttle/application_controller' 3 | 4 | module Tuttle 5 | class ActiveJobController < ApplicationController 6 | 7 | def index 8 | @job_classes = ActiveJob::Base.descendants # May want to not includ ApplicationJob if it exists 9 | 10 | # TODO: could also look to see if GlobalID is available 11 | # https://github.com/rails/globalid 12 | 13 | # See http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html 14 | 15 | # TODO: detect available backend libraries 16 | # TODO: catalog the .descendants of ActiveJob::Base 17 | # - list the queue_name and queue_adapter per job 18 | 19 | # == Active Job adapters 20 | # 21 | # Active Job has adapters for the following queueing backends: 22 | # 23 | # * {Backburner}[https://github.com/nesquena/backburner] 24 | # * {Delayed Job}[https://github.com/collectiveidea/delayed_job] 25 | # * {Qu}[https://github.com/bkeepers/qu] 26 | # * {Que}[https://github.com/chanks/que] 27 | # * {queue_classic}[https://github.com/QueueClassic/queue_classic] 28 | # * {Resque 1.x}[https://github.com/resque/resque/tree/1-x-stable] 29 | # * {Sidekiq}[http://sidekiq.org] 30 | # * {Sneakers}[https://github.com/jondot/sneakers] 31 | # * {Sucker Punch}[https://github.com/brandonhilkert/sucker_punch] 32 | # * {Active Job Async Job}[http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/AsyncAdapter.html] 33 | # * {Active Job Inline}[http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/InlineAdapter.html] 34 | # 35 | # === Backends Features 36 | # 37 | # | | Async | Queues | Delayed | Priorities | Timeout | Retries | 38 | # |-------------------|-------|--------|------------|------------|---------|---------| 39 | # | Backburner | Yes | Yes | Yes | Yes | Job | Global | 40 | # | Delayed Job | Yes | Yes | Yes | Job | Global | Global | 41 | # | Qu | Yes | Yes | No | No | No | Global | 42 | # | Que | Yes | Yes | Yes | Job | No | Job | 43 | # | queue_classic | Yes | Yes | Yes* | No | No | No | 44 | # | Resque | Yes | Yes | Yes (Gem) | Queue | Global | Yes | 45 | # | Sidekiq | Yes | Yes | Yes | Queue | No | Job | 46 | # | Sneakers | Yes | Yes | No | Queue | Queue | No | 47 | # | Sucker Punch | Yes | Yes | Yes | No | No | No | 48 | # | Active Job Async | Yes | Yes | Yes | No | No | No | 49 | # | Active Job Inline | No | Yes | N/A | N/A | N/A | N/A | 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /app/controllers/tuttle/active_model_serializers_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_dependency 'tuttle/application_controller' 3 | 4 | module Tuttle 5 | class ActiveModelSerializersController < ApplicationController 6 | 7 | def index 8 | @models = ActiveRecord::Base.descendants 9 | @models.sort_by!(&:name) 10 | 11 | @serializers = ::ActiveModel::Serializer.descendants 12 | @serializers.sort_by!(&:name) 13 | 14 | if defined?(ActiveModelSerializers) 15 | render 'index' 16 | else 17 | render 'index9' 18 | end 19 | end 20 | 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/controllers/tuttle/active_support_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_dependency 'tuttle/application_controller' 3 | 4 | module Tuttle 5 | class ActiveSupportController < ApplicationController 6 | 7 | def index 8 | end 9 | 10 | def dependencies 11 | end 12 | 13 | def inflectors 14 | @test_word = params[:test_word] || '' 15 | 16 | @plurals = ActiveSupport::Inflector.inflections.plurals 17 | @singulars = ActiveSupport::Inflector.inflections.singulars 18 | @uncountables = ActiveSupport::Inflector.inflections.uncountables 19 | @humans = ActiveSupport::Inflector.inflections.humans 20 | @acronyms = ActiveSupport::Inflector.inflections.acronyms 21 | end 22 | 23 | def time_zones 24 | end 25 | 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/controllers/tuttle/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module Tuttle 3 | class ApplicationController < ActionController::Base 4 | abstract! 5 | protect_from_forgery with: :exception 6 | 7 | before_action :check_reload_status 8 | around_action :set_locale 9 | 10 | private 11 | 12 | def check_reload_status 13 | return unless Tuttle::Engine.reload_needed && !Rails.configuration.eager_load 14 | Tuttle::Engine.logger.warn('Eager-loading application') 15 | ActiveSupport::Notifications.instrument 'tuttle.perform_eager_load' do 16 | begin 17 | Rails.application.eager_load! 18 | rescue 19 | Tuttle::Engine.logger.warn('Failed to eager-load application') 20 | ensure 21 | Tuttle::Engine.reload_needed = false 22 | end 23 | end 24 | end 25 | 26 | def set_locale 27 | if params[:hl] && I18n.locale_available?(params[:hl]) 28 | I18n.with_locale(params[:hl]) do 29 | yield 30 | end 31 | else 32 | yield 33 | end 34 | end 35 | 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /app/controllers/tuttle/cancancan_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_dependency 'tuttle/application_controller' 3 | 4 | module Tuttle 5 | class CancancanController < ApplicationController 6 | 7 | def index 8 | @cancan_user = current_user || User.new 9 | @ability = Ability.new(@cancan_user) 10 | @rules = @ability.instance_variable_get('@rules') 11 | end 12 | 13 | def rule_tester 14 | @models = ActiveRecord::Base.descendants.reject(&:abstract_class) 15 | @action = params[:action_name] || 'read' 16 | @subject = find_subject(params[:subject_class], params[:subject_id]) 17 | @cancan_user = current_user || User.new 18 | @ability = Ability.new(@cancan_user) 19 | end 20 | 21 | private 22 | 23 | def find_subject(subject_class, subject_id) 24 | subject_klass = @models.detect { |x| x.name == subject_class } if subject_class.present? 25 | subject_klass.find_or_initialize_by(:id => subject_id) if subject_klass 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/controllers/tuttle/devise_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_dependency 'tuttle/application_controller' 3 | require 'devise/version' if defined?(Devise) 4 | 5 | module Tuttle 6 | class DeviseController < ApplicationController 7 | 8 | def index 9 | end 10 | 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/controllers/tuttle/execjs_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_dependency 'tuttle/application_controller' 3 | require 'execjs/version' if defined?(::ExecJS) 4 | 5 | module Tuttle 6 | class ExecjsController < ApplicationController 7 | 8 | def index 9 | end 10 | 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/controllers/tuttle/gems_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_dependency 'tuttle/application_controller' 3 | 4 | module Tuttle 5 | class GemsController < ApplicationController 6 | 7 | def index 8 | @gemspecs = Bundler.rubygems.all_specs.sort_by(&:name) 9 | end 10 | 11 | def http_clients 12 | end 13 | 14 | def json 15 | end 16 | 17 | def get_process_mem # rubocop:disable Naming/AccessorMethodName 18 | require 'get_process_mem' 19 | require 'get_process_mem/version' 20 | @memory_self = GetProcessMem.new 21 | @memory_parent = GetProcessMem.new(Process.ppid) 22 | end 23 | 24 | def other 25 | end 26 | 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/controllers/tuttle/home_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_dependency 'tuttle/application_controller' 3 | require 'tuttle/version' 4 | 5 | module Tuttle 6 | class HomeController < ApplicationController 7 | 8 | def index 9 | end 10 | 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/controllers/tuttle/i18n_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_dependency 'tuttle/application_controller' 3 | 4 | module Tuttle 5 | class I18nController < ApplicationController 6 | 7 | before_action :set_sorted_locales 8 | 9 | def index 10 | end 11 | 12 | def localize 13 | end 14 | 15 | def translations 16 | @translations = I18n.backend.send(:translations) 17 | end 18 | 19 | private 20 | 21 | def set_sorted_locales 22 | @sorted_locales = [I18n.default_locale] + 23 | (I18n.available_locales.map(&:to_s).sort.map(&:to_sym) - [I18n.default_locale]) 24 | end 25 | 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/controllers/tuttle/rack_attack_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_dependency 'tuttle/application_controller' 3 | 4 | module Tuttle 5 | class RackAttackController < ApplicationController 6 | 7 | def index 8 | require 'rack/attack/version' 9 | if ::Rack::Attack::VERSION < '6' 10 | render plain: 'Tuttle Rack::Attack support requires version 6.x' && return 11 | end 12 | 13 | default_keys = %w[rack.attack.matched rack.attack.match_discriminator rack.attack.match_type rack.attack.match_data] 14 | rack_attack_env_vars = request.env.select { |k, _v| k.to_s.start_with?('rack.attack') } 15 | @rack_attack_env_settings = (rack_attack_env_vars.keys + default_keys).uniq 16 | 17 | @throttles = Rack::Attack.throttles 18 | @safelists = Rack::Attack.safelists 19 | @blocklists = Rack::Attack.blocklists 20 | # TODO: tracks are no longer exposed 21 | # @tracks = Rack::Attack.tracks 22 | @tracks = [] 23 | 24 | @asn_listeners = ActiveSupport::Notifications.notifier.listeners_for("rack.attack") 25 | end 26 | 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/controllers/tuttle/rack_mini_profiler_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_dependency 'tuttle/application_controller' 3 | 4 | module Tuttle 5 | class RackMiniProfilerController < ApplicationController 6 | 7 | def index 8 | return redirect_to gems_other_path unless defined?(::Rack::MiniProfiler::Config) 9 | 10 | require_dependency 'tuttle/presenters/rack_mini_profiler/client_settings' 11 | @mp_config = ::Rack::MiniProfiler.config 12 | @mp_client_settings = Tuttle::Presenters::RackMiniProfiler::ClientSettings.new(request.env) 13 | end 14 | 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/controllers/tuttle/rails_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'rails/generators' 3 | require_dependency 'tuttle/application_controller' 4 | require_dependency 'tuttle/presenters/action_dispatch/routing/route_wrapper' 5 | require_dependency 'tuttle/presenters/active_support/callbacks' 6 | require_dependency 'tuttle/presenters/active_record/reflection_presenter' 7 | 8 | module Tuttle 9 | class RailsController < ApplicationController 10 | 11 | def index 12 | @config_options = Tuttle::ConfigurationRegistry.data.to_a.sort_by!(&:first) 13 | @all_load_paths = if Rails::VERSION::STRING < "6" 14 | (Rails.application.send(:_all_load_paths) + ActiveSupport::Dependencies.autoload_paths).uniq rescue [] 15 | else 16 | (Rails.application.send(:_all_load_paths, true) + ActiveSupport::Dependencies.autoload_paths).uniq rescue [] 17 | end 18 | end 19 | 20 | def controllers 21 | @routes = Rails.application.routes.routes.collect do |route| 22 | Tuttle::Presenters::ActionDispatch::Routing::RouteWrapper.new(route) 23 | end 24 | 25 | # It seems likely that .descendants will work best when Tuttle and Rails classes are not modified 26 | # but both approaches also require eager_load to be true 27 | # @controllers = ObjectSpace.each_object(::Class).select {|klass| klass < ActionController::Base } 28 | @controllers = ActionController::Base.descendants 29 | @controllers.reject! do |controller| 30 | controller.abstract? || controller <= Tuttle::ApplicationController 31 | end 32 | 33 | # Rails 5.1 introduced Rails::ApplicationController which really should be abstract 34 | if defined?(Rails::ApplicationController) 35 | @controllers.reject! { |controller| controller == Rails::ApplicationController } 36 | end 37 | 38 | # TODO: some classes do not have names, why? 39 | # @controllers.sort_by!(&:name) 40 | end 41 | 42 | def engines 43 | @engines = Rails::Engine.subclasses.map(&:instance) 44 | end 45 | 46 | def generators 47 | Rails::Generators.lookup! if params[:load_all_generators] == 'true' 48 | @generators = Rails::Generators.subclasses.group_by(&:base_name) 49 | end 50 | 51 | def models 52 | @models = ActiveRecord::Base.descendants.reject { |model| model.abstract_class? || !model.table_exists? } 53 | @models.sort_by!(&:name) 54 | end 55 | 56 | def database 57 | @conn = ActiveRecord::Base.connection 58 | @data_sources = @conn.respond_to?(:data_sources) ? @conn.data_sources : @conn.tables 59 | end 60 | 61 | def schema_cache 62 | @schema_cache_filename = File.join(Rails.application.config.paths['db'].first, 'schema_cache.dump') 63 | if File.file?(@schema_cache_filename) 64 | @schema_cache = Marshal.load(File.binread(@schema_cache_filename)) 65 | end 66 | # TODO: wrap in a facade and handle unloaded file 67 | @schema_cache ||= ActiveRecord::ConnectionAdapters::SchemaCache.new(nil) 68 | 69 | # TODO: consider allowing a schema cache clear! 70 | # TODO: consider allowing a schema_cache.dump reload 71 | # if @schema_cache.version && params[:reload_schema_cache_dump] 72 | # ActiveRecord::Base.connection.schema_cache = @schema_cache.dup 73 | # end 74 | 75 | @connection_schema_cache = ActiveRecord::Base.connection.schema_cache 76 | # Note: Rails 5 should also support ActiveRecord::Base.connection_pool.respond_to?(:schema_cache) 77 | end 78 | 79 | def helpers 80 | # TODO: Rails.application.helpers.instance_methods 81 | # helper_symbol = Rails.application.helpers.instance_methods.first 82 | # Rails.application.helpers.instance_method(helper_symbol).owner 83 | # Rails.application.helpers.instance_method(helper_symbol).parameters 84 | @helpers = ::ApplicationController.send(:modules_for_helpers, [:all]) 85 | end 86 | 87 | def assets 88 | @sprockets_env = Rails.application.assets 89 | @assets_config = Rails.application.config.assets 90 | # TODO: revisit detection of "engines" which are classified as processors, transformers, etc. 91 | end 92 | 93 | def routes 94 | @routes = Rails.application.routes.routes.collect do |route| 95 | Tuttle::Presenters::ActionDispatch::Routing::RouteWrapper.new(route) 96 | end 97 | if params[:recognize_path] 98 | @path_to_recognize = params[:recognize_path] 99 | @recognized_paths = recognize_paths(params[:recognize_path]) 100 | end 101 | # TODO: include engine-mounted routes 102 | end 103 | 104 | def instrumentation 105 | @events = Tuttle::Instrumenter.events 106 | @event_counts = Tuttle::Instrumenter.event_counts 107 | end 108 | 109 | def cache 110 | @cache = Rails.cache 111 | # TODO: make cache instrumentation controllable - this will automatically turn in on in Rails < 4.2 112 | # Instrumentation is always on in Rails 4.2+ 113 | # if Rails::VERSION::STRING =~ /^4\.1\./ && !ActiveSupport::Cache::Store.instrument 114 | # ActiveSupport::Cache::Store.instrument = true 115 | # end 116 | # @cache_events = Tuttle::Instrumenter.events.select {|e| /cache_(read|write)\.active_support/ =~ e.name } 117 | # @tuttle_cache_events = Tuttle::Instrumenter.cache_events 118 | end 119 | 120 | private 121 | 122 | def recognize_paths(path) 123 | results = {} 124 | %i[get post put delete patch].each {|method| results[method] = recognize_path(path, method: method)} 125 | results 126 | end 127 | 128 | # a version that handles engines - based on https://gist.github.com/jtanium/6114632 129 | # it's possible that multiple engines could handle a particular path. So we will 130 | # capture each of them 131 | def recognize_path(path, options) 132 | recognized_paths = [] 133 | recognized_paths << Rails.application.routes.recognize_path(path, options) 134 | rescue ActionController::RoutingError => exception 135 | # The main app didn't recognize the path, try the engines... 136 | Rails::Engine.subclasses.each do |engine| 137 | engine_instance = engine.instance 138 | engine_class = engine_instance.class 139 | begin 140 | recognized_path = engine_instance.routes.recognize_path(path, options) 141 | recognized_path[:engine] = engine_class 142 | recognized_paths << recognized_path 143 | rescue ActionController::RoutingError 144 | Tuttle::Engine.logger.info("Routing error recognizing path for #{path}") 145 | end 146 | end 147 | 148 | recognized_paths.empty? ? [{ error: exception.message }] : recognized_paths 149 | end 150 | 151 | end 152 | end 153 | -------------------------------------------------------------------------------- /app/controllers/tuttle/request_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_dependency 'tuttle/application_controller' 3 | 4 | module Tuttle 5 | class RequestController < ApplicationController 6 | 7 | def index 8 | @session_hash = session.to_hash 9 | @cookies_hash = request.cookie_jar.to_h 10 | end 11 | 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/controllers/tuttle/ruby_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_dependency 'tuttle/application_controller' 4 | 5 | module Tuttle 6 | class RubyController < ApplicationController 7 | 8 | ENV_FILTERS = [/.*_(URL|PASSWORD|KEY|KEY_BASE|AUTHENTICATION)$/].freeze 9 | 10 | def index 11 | # TODO: need better filter for sensitive values. this covers DB-style URLs with passwords, passwords, and keys 12 | env_hash = ENV.to_hash 13 | if Rails::VERSION::MAJOR < 6 14 | env_hash = ActionDispatch::Http::ParameterFilter.new(ENV_FILTERS).filter(env_hash) unless params[:nofilter] 15 | else 16 | env_hash = ActiveSupport::ParameterFilter.new(ENV_FILTERS).filter(env_hash) unless params[:nofilter] 17 | end 18 | @filtered_env = env_hash.sort 19 | end 20 | 21 | def tuning 22 | @gc_enabled = (GC.disable ? false : GC.enable) 23 | 24 | # taken verbatim from the ruby 2.2 man page 25 | @gc_params = { 26 | 'RUBY_GC_HEAP_INIT_SLOTS' => 'Initial allocation slots. Introduced in Ruby 2.1, default: 10000.', 27 | 'RUBY_GC_HEAP_FREE_SLOTS' => 'Prepare at least this amount of slots after GC. Allocate this number slots if there are not enough slots. Introduced in Ruby 2.1, default: 4096', 28 | 'RUBY_GC_HEAP_GROWTH_FACTOR' => 'Increase allocation rate of heap slots by this factor. Introduced in Ruby 2.1, default: 1.8, minimum: 1.0 (no growth)', 29 | 'RUBY_GC_HEAP_GROWTH_MAX_SLOTS' => 'Allocation rate is limited to this number of slots, preventing excessive allocation due to RUBY_GC_HEAP_GROWTH_FACTOR. Introduced in Ruby 2.1, default: 0 (no limit)', 30 | 'RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR' => 'Perform a full GC when the number of old objects is more than R * N, where R is this factor and N is the number of old objects after the last full GC. Introduced in Ruby 2.1.1, default: 2.0', 31 | 'RUBY_GC_MALLOC_LIMIT' => 'The initial limit of young generation allocation from the malloc-family. GC will start when this limit is reached. Default: 16MB', 32 | 'RUBY_GC_MALLOC_LIMIT_MAX' => 'The maximum limit of young generation allocation from malloc before GC starts. Prevents excessive malloc growth due to RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR. Introduced in Ruby 2.1, default: 32MB.', 33 | 'RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR' => 'Increases the limit of young generation malloc calls, reducing GC frequency but increasing malloc growth until RUBY_GC_MALLOC_LIMIT_MAX is reached. Introduced in Ruby 2.1, default: 1.4, minimum: 1.0 (no growth)', 34 | 'RUBY_GC_OLDMALLOC_LIMIT' => 'The initial limit of old generation allocation from malloc, a full GC will start when this limit is reached. Introduced in Ruby 2.1, default: 16MB', 35 | 'RUBY_GC_OLDMALLOC_LIMIT_MAX' => 'The maximum limit of old generation allocation from malloc before a full GC starts. Prevents excessive malloc growth due to RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR. Introduced in Ruby 2.1, default: 128MB', 36 | 'RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR' => 'Increases the limit of old generation malloc allocation, reducing full GC frequency but increasing malloc growth until RUBY_GC_OLDMALLOC_LIMIT_MAX is reached. Introduced in Ruby 2.1, default: 1.2, minimum: 1.0 (no growth)' 37 | } 38 | end 39 | 40 | def miscellaneous 41 | end 42 | 43 | def constants 44 | # Global constants minus a few deprecated constants (to prevent warnings/errors) 45 | @constants = (Object.constants - %i[Bignum Fixnum NIL TRUE FALSE TimeoutError Data SortedSet]). 46 | sort. 47 | map { |sym| [sym, Object.const_get(sym).class, Object.const_get(sym)] }. 48 | reject { |_sym, klass, _val| klass == Class || klass == Module } 49 | end 50 | 51 | def extensions 52 | @obj_extensions = Object.methods.select { |meth| Object.method(meth).source_location }. 53 | sort. 54 | map { |meth| [meth, *Object.method(meth).source_location] } 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /app/helpers/tuttle/application_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module Tuttle 3 | module ApplicationHelper 4 | BUNDLER_GEM_PATHS_REGEX = %r{(#{Bundler.rubygems.gem_dir}|#{File.realpath(Bundler.rubygems.gem_dir)})+(/bundler)*/gems} 5 | 6 | # Display true/false label with text overrides 7 | def truth_label(is_true, true_label = 'true', false_label = 'false') 8 | content_tag(:span, is_true ? true_label : false_label, 9 | class: ['label', is_true ? 'label-success' : 'label-danger']) 10 | end 11 | 12 | # Display label if condition is true 13 | def true_label(is_true, true_label = 'true') 14 | content_tag(:span, true_label, class: %w[label label-success]) if is_true 15 | end 16 | 17 | def tuttle_redacted(enumarator) 18 | enumarator.collect do |key, value| 19 | yield key, redact_by_key(key, value) 20 | end 21 | end 22 | 23 | def main_app_root_path 24 | main_app.respond_to?(:root_path) ? main_app.root_path : '/' 25 | end 26 | 27 | def main_app_root_url 28 | main_app.respond_to?(:root_url) ? main_app.root_url : '/' 29 | end 30 | 31 | def link_to_rails_guide(file, title) 32 | content_tag(:a, title, :href => rails_guides_versioned_url(file + '.html')) 33 | end 34 | 35 | def rails_guides_versioned_url(path) 36 | "http://guides.rubyonrails.org/v#{Tuttle::VersionDetector.rails_major_minor}/#{path}" 37 | end 38 | 39 | def display_path(path) 40 | return if path.blank? 41 | display_location = 42 | if path.start_with?(Rails.root.to_s) 43 | path.sub(Rails.root.to_s, "$RAILS_ROOT") 44 | elsif File.realpath(path).start_with?(File.realpath(Bundler.rubygems.gem_dir)) 45 | File.realpath(path).sub(BUNDLER_GEM_PATHS_REGEX, "$GEMS") 46 | else 47 | path 48 | end 49 | expanded_path = File.expand_path(path) 50 | content_tag(:span, display_location, :class => 'tuttle-path', :data => { :initial => path, :expanded => expanded_path }) 51 | end 52 | 53 | def display_source_locaction(path, line) 54 | return content_tag(:span, "Unknown", :class => 'tuttle-path') if path.nil? 55 | 56 | display_location = file_location(path) 57 | expanded_path = File.expand_path(path) 58 | content_tag(:span, :class => 'tuttle-path', :data => { :initial => path, :expanded => expanded_path }) do 59 | "#{display_location}##{line}" 60 | end 61 | end 62 | 63 | def file_location(path) 64 | if path.start_with?(Rails.root.to_s) 65 | path.gsub(Rails.root.to_s, "$RAILS_ROOT") 66 | elsif path.start_with?(RbConfig::CONFIG['rubylibdir']) 67 | path.gsub(RbConfig::CONFIG['rubylibdir'], "$RUBY_LIB_DIR") 68 | elsif path == "" || path == "" 69 | path 70 | elsif File.realpath(path).start_with?(File.realpath(Bundler.rubygems.gem_dir)) 71 | File.realpath(path).gsub(BUNDLER_GEM_PATHS_REGEX, "$GEMS") 72 | else 73 | path 74 | end 75 | end 76 | 77 | def value_inspect(val, hide_nil: false) 78 | case val 79 | when NilClass 80 | content_tag(:code, val.inspect) unless hide_nil 81 | when String 82 | content_tag(:code, val.inspect) 83 | when Symbol 84 | content_tag(:code, val.inspect) 85 | else 86 | val.inspect 87 | end 88 | end 89 | 90 | private 91 | 92 | def redact_by_key(key, value) 93 | case key 94 | when 'password', /(_secret|_credentials)/ 95 | '--HIDDEN--' 96 | else 97 | value 98 | end 99 | end 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /app/models/tuttle/configuration_registry.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module Tuttle 3 | class ConfigurationRegistry 4 | 5 | class << self 6 | def data 7 | @registry ||= load_data 8 | end 9 | 10 | private 11 | 12 | def load_data 13 | data_dir = Tuttle::Engine.instance.paths['config'].first 14 | 15 | data = YAML.load_file(File.expand_path('rails_config_base.yml', data_dir)) 16 | if Rails::VERSION::MAJOR == 6 17 | data.merge!(YAML.load_file(File.expand_path('rails_config_v6.x.yml', data_dir))) 18 | end 19 | 20 | data 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/models/tuttle/version_detector.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module Tuttle 3 | class VersionDetector 4 | 5 | def self.rails_major_minor 6 | @rails_major_minor ||= Rails::VERSION::STRING.slice(0, 3) 7 | end 8 | 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/views/tuttle/active_job/index.html.erb: -------------------------------------------------------------------------------- 1 |

Active Job Overview

2 | 3 |
4 |
Active Job Version
<%= ActiveJob::VERSION::STRING %>
5 |
6 | 7 |

Config Settings

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
SettingValueDescription
config.active_job<%= Rails.configuration.active_job %>All settings for config.active_job
config.active_job.queue_adapter<%= Rails.configuration.active_job.queue_adapter %>
config.active_job.queue_name_prefix<%= Rails.configuration.active_job.queue_name_prefix %>An optional name to prepend to job queue names
30 | 31 |

Job Classes

32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | <% @job_classes.each do |job_klass| %> 40 | 41 | 42 | 43 | <% # TODO: queue_name can be a Proc set with `queue_as` %> 44 | 45 | 46 | 47 | <% end %> 48 |
ClassQueue AdapterQueueperform arguments
<%= job_klass.name %><%= job_klass.queue_adapter %><%= job_klass.queue_name %><%= job_klass.instance_method(:perform).parameters.map { |_req, var| var }.join(', ') %>
49 | -------------------------------------------------------------------------------- /app/views/tuttle/active_model_serializers/index.html.erb: -------------------------------------------------------------------------------- 1 |

ActiveModelSerializers Configuration

2 | 3 |
4 |
AMS Version
<%= ActiveModel::Serializer::VERSION %>
5 |
6 | 7 |

Config Settings

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | <% [:collection_serializer, :serializer_lookup_enabled, :adapter, :schema_path, 22 | :jsonapi_version, :jsonapi_resource_type, :jsonapi_toplevel_meta, :jsonapi_include_toplevel_object].each do |config_method| %> 23 | 24 | 25 | 26 | 27 | <% end %> 28 |
SettingValue
ActiveModelSerializers.config.perform_caching<%= truth_label(ActiveModelSerializers.config.perform_caching) %>
ActiveModelSerializers.config.cache_store<%= ActiveModelSerializers.config.cache_store.inspect %>
<%= config_method %><%= ActiveModel::Serializer.config.send(config_method).inspect %>
29 | 30 |

Serializers

31 | 32 | 33 | 34 | 35 | 36 | <% @serializers.each do |serializer_class| %> 37 | 38 | 39 | 53 | 54 | <% end %> 55 |
SerializerConfiguration
<%= serializer_class %> 40 | Attributes: <%= serializer_class._attributes_data.keys.inspect %>
41 | Links: <%= serializer_class._links.keys.inspect %>
42 | Reflections: <%= serializer_class._reflections.collect(&:name).inspect %>
43 | Type: <%= serializer_class._type.inspect %>
44 | Cached?: <%= truth_label(serializer_class._cache.present?) %>
45 | <% if serializer_class._cache.present? %> 46 | Cache Key: <%= serializer_class._cache_key.inspect %>
47 | Cache Only: <%= serializer_class._cache_only.inspect %>
48 | Cache Except: <%= serializer_class._cache_except.inspect %>
49 | Cache Options: <%= serializer_class._cache_options.inspect %>
50 | <% end %> 51 | Instance Methods: <%= serializer_class.instance_methods(false).inspect %>
52 |
56 | 57 | <% if false # This seems to be in flux %> 58 |

Adapters

59 | 60 | 61 | 62 | 63 | 64 | <% @adapter_map.each do |k, v| %> 65 | 66 | 67 | 68 | 69 | <% end %> 70 |
NameAdapter
<%= k %><%= v.inspect %>
71 | <% end %> 72 | 73 |

Active Record Models

74 | 75 | 76 | 77 | 78 | 79 | <% 80 | @models.each do |ar_model| 81 | model_serializer = ActiveModel::Serializer.get_serializer_for(ar_model) 82 | %> 83 | 84 | 85 | 86 | 87 | <% end %> 88 |
NameAdapter
<%= ar_model %><%= model_serializer.inspect %>
89 | -------------------------------------------------------------------------------- /app/views/tuttle/active_model_serializers/index9.html.erb: -------------------------------------------------------------------------------- 1 |

ActiveModelSerializers Configuration

2 | 3 |
4 |
AMS Version
<%= ActiveModel::Serializer::VERSION %>
5 |
6 | 7 |

Config Settings

8 | 9 | 10 | 11 | 12 | 13 | 14 | <% ActiveModel::Serializer::CONFIG.each do |k, v| %> 15 | 16 | 17 | 18 | 19 | 20 | <% end %> 21 |
SettingValueDescription
<%= k %><%= v.inspect %>
22 | 23 |

Serializers

24 | 25 | 26 | 27 | 28 | 29 | <% @serializers.each do |serializer_class| %> 30 | 31 | 32 | 37 | 38 | <% end %> 39 |
SerializerConfiguration
<%= serializer_class %> 33 | Root: <%= serializer_class._root.inspect %>
34 | Attributes: <%= serializer_class._attributes.inspect %>
35 | Associations: <%= serializer_class._associations.keys.inspect %>
36 |
40 | 41 |

Active Record Models with Serializers

42 |

Note: Models do not need to have an assoicated Serializer to be rendered with a serializer.

43 | 44 | 45 | 46 | 47 | 48 | <% 49 | @models.each do |ar_model| 50 | begin 51 | next if ar_model.abstract_class? 52 | ar_instance = ar_model.new rescue nil 53 | next unless ar_instance 54 | model_serializer = ActiveModel::Serializer.serializer_for(ar_instance) 55 | next unless model_serializer 56 | %> 57 | 58 | 59 | 60 | 61 | 62 | <% 63 | rescue 64 | # ignore models that cause exceptions 65 | end 66 | end 67 | %> 68 |
ModelSerializer
<%= ar_model %><%= model_serializer.inspect %>
69 | -------------------------------------------------------------------------------- /app/views/tuttle/active_support/dependencies.html.erb: -------------------------------------------------------------------------------- 1 |

Active Support Dependencies

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
SettingValueDescription
warnings_on_first_load<%= truth_label(ActiveSupport::Dependencies.warnings_on_first_load) %>Should we turn on Ruby warnings on the first load of dependent files?
mechanism<%= ActiveSupport::Dependencies.mechanism %>Should we load files or require them?
22 | 23 |

Autoload Paths <%= ActiveSupport::Dependencies.autoload_paths.size %> directories

24 |

25 | The set of directories from which we may automatically load files. Files 26 | under these directories will be reloaded on each request in development mode, 27 | unless the directory also appears in autoload_once_paths. 28 |

29 | 34 | 35 |

Autoload Once Paths <%= ActiveSupport::Dependencies.autoload_once_paths.size %> directories

36 |

37 | The set of directories from which automatically loaded constants are loaded 38 | only once. All directories in this set must also be present in +autoload_paths+. 39 |

40 | 45 | 46 |

Explicitly Unloadable Constants <%= ActiveSupport::Dependencies.explicitly_unloadable_constants.size %> constants

47 |

48 | An array of constant names that need to be unloaded on every request. Used 49 | to allow arbitrary constants to be marked for unloading. 50 |

51 | 56 | 57 |

Autoloaded Constants <%= ActiveSupport::Dependencies.autoloaded_constants.size %> constants

58 |

59 | An array of qualified constant names that have been loaded. 60 |

61 | 66 | 67 |

Loaded files <%= ActiveSupport::Dependencies.history.size %> files

68 | 73 | -------------------------------------------------------------------------------- /app/views/tuttle/active_support/index.html.erb: -------------------------------------------------------------------------------- 1 |

Active Support Overview

2 | 3 |
4 |
Active Support Version
<%= ActiveSupport::VERSION::STRING %>
5 |
6 | 7 |

Config Settings

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
SettingValueDescription
config.active_support<%= Rails.configuration.active_support %>All settings for config.active_support
config.active_support.bare<%= truth_label(Rails.configuration.active_support.bare) %>enables or disables the loading of active_support/all when booting Rails. Defaults to nil, which means active_support/all is loaded.
config.active_support.test_order<%= Rails.configuration.active_support.test_order %>sets the order that test cases are executed. Possible values are :sorted and :random. Currently defaults to :sorted. In Rails 5.0, the default will be changed to :random instead.
config.active_support.escape_html_entities_in_json<%= Rails.configuration.active_support.escape_html_entities_in_json %>enables or disables the escaping of HTML entities in JSON serialization. Defaults to false.
config.active_support.use_standard_json_time_format<%= Rails.configuration.active_support.use_standard_json_time_format %>enables or disables serializing dates to ISO 8601 format. Defaults to true.
config.active_support.time_precision<%= Rails.configuration.active_support.time_precision %>sets the precision of JSON encoded time values. Defaults to 3.
ActiveSupport::Logger.silencer<%= truth_label(ActiveSupport::Logger.silencer) %>set to false to disable the ability to silence logging in a block. The default is true.
ActiveSupport::Cache::Store.logger<%= ActiveSupport::Cache::Store.logger %>specifies the logger to use within cache store operations
ActiveSupport::Deprecation.silenced<%= truth_label(ActiveSupport::Deprecation.silenced) %>sets whether or not to display deprecation warnings
config.active_support.deprecation<%= Rails.configuration.active_support.deprecation %>specifies which deprecation method to use. the default values are <%= ActiveSupport::Deprecation::DEFAULT_BEHAVIORS.keys %>
current deprecation behavior<%= ActiveSupport::Deprecation::DEFAULT_BEHAVIORS.key(ActiveSupport::Deprecation.behavior.first) || 'custom' %>The current runtime value of the depreaction behavior
70 | -------------------------------------------------------------------------------- /app/views/tuttle/active_support/inflectors.html.erb: -------------------------------------------------------------------------------- 1 |

ActiveSupport::Inflection

2 | 3 |

Example

4 |
5 |
6 | 7 |
8 | 9 |
10 |
11 |
12 |
13 | 14 |
15 |
16 |
17 | 18 | <%- unless @test_word.blank? %> 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
VariationValue
pluralize<%= @test_word.pluralize %>
singularize<%= @test_word.singularize %>
humanize<%= @test_word.humanize %>
titleize<%= @test_word.titleize %>
camelize<%= @test_word.camelize %>
classify<%= @test_word.classify %>
dasherize<%= @test_word.dasherize %>
deconstantize<%= @test_word.deconstantize %>
demodulize<%= @test_word.demodulize %>
foreign_key<%= @test_word.foreign_key %>
parameterize<%= @test_word.parameterize %>
tableize<%= @test_word.tableize %>
underscore<%= @test_word.underscore %>
36 | <%- end %> 37 | 38 |

Inflector Rules

39 | 40 |
41 | @plurals = <%= @plurals.inspect %>
42 | 
43 | @singulars = <%= @singulars.inspect %>
44 | 
45 | @uncountables = <%= @uncountables.inspect %>
46 | 
47 | @humans = <%= @humans.inspect %>
48 | 
49 | @acronyms = <%= @acronyms.inspect %>
50 | 
51 | -------------------------------------------------------------------------------- /app/views/tuttle/active_support/time_zones.html.erb: -------------------------------------------------------------------------------- 1 |

ActiveSupport Time Zones

2 | 3 | 4 | 5 |

All TimeZones

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | <% ActiveSupport::TimeZone.all.each do |tz| %> 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | <% end %> 24 |
NameUTC Offsetto_sNowTZInfo
<%= tz.name %><%= tz.utc_offset %><%= tz.to_s %><%= tz.now %><%= tz.tzinfo %>
25 | -------------------------------------------------------------------------------- /app/views/tuttle/cancancan/_rule_table.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | <%- relevant_rules = @ability.send(:relevant_rules_for_match, @action.to_sym, @subject) -%> 13 | <% rules.each do |rule| %> 14 | 15 | <% relevant = @action && @subject && relevant_rules.include?(rule) -%> 16 | 17 | 18 | 19 | 20 | 21 | <%- subjects = rule.instance_variable_get('@subjects') %> 22 | 27 | 28 | 29 | 30 | <% end %> 31 |
Relevant?Pass?Can?ActionsSubjectConditionsBlock?
<%= 'true' if relevant %><%= 'true' if relevant && rule.matches_conditions?(@action.to_sym, @subject, nil) %><%= rule.base_behavior ? 'Can':'Cannot' %><%= rule.actions.inspect %> 23 | <% subjects.each do |s| %> 24 | <%= s %> 25 | <% end %> 26 | <%= rule.conditions unless rule.conditions.empty? %><%= 'X' if rule.instance_variable_get('@block').present? %>
32 | -------------------------------------------------------------------------------- /app/views/tuttle/cancancan/index.html.erb: -------------------------------------------------------------------------------- 1 |

CanCanCan Configuration

2 | 3 | 8 | 9 |
10 |
11 |
12 |
CanCanCan Version
<%= CanCan::VERSION %>
13 |
Current User
<%= truncate(@cancan_user.inspect, length: 100) %>
14 |
Current Ability
<%= truncate(@ability.inspect, length: 100) %>
15 |
Aliased Actions
<%= @ability.aliased_actions %>
16 |
17 |
18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | <% @ability.instance_variable_get('@rules').each do |rule| %> 29 | 30 | 31 | 32 | <%- subjects = rule.instance_variable_get('@subjects') %> 33 | 38 | 39 | 40 | 41 | <% end %> 42 |
Can?ActionsSubjectConditionsBlock?
<%= rule.base_behavior ? 'Can':'Cannot' %><%= rule.actions.inspect %> 34 | <% subjects.each do |s| %> 35 | <%= s %> 36 | <% end %> 37 | <%= rule.conditions %><%= rule.instance_variable_get('@block').present? %>
43 |
44 | 45 |
46 | -------------------------------------------------------------------------------- /app/views/tuttle/cancancan/rule_tester.html.erb: -------------------------------------------------------------------------------- 1 |

CanCanCan

2 | 3 | 8 |
9 | 10 |
11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 |
20 | <%= select_tag('subject_class',options_for_select(@models.collect(&:name).sort, params[:subject_class]),:include_blank=>true, :class => 'form-control') %> 21 |
22 |
23 |
24 | 25 |
26 | 27 |
28 |
29 |
30 |
31 | 32 |
33 |
34 |
35 | 36 | <% if @action && @subject %> 37 |

38 | <%= "User.can?(:#{@action}, #{@subject.class.name}.find(#{@subject.id}) ) = #{@ability.can?(@action, @subject)}" if @subject.id %> 39 | <%= "User.can?(:#{@action}, #{@subject.class.name}.new() ) = #{@ability.can?(@action, @subject)}" unless @subject.id %> 40 |

41 | <% end %> 42 | 43 | <%- relevant_rules = @ability.send(:relevant_rules_for_match, @action.to_sym, @subject) -%> 44 | 45 |

Relevant Rules

46 | <%= render :partial => 'rule_table', :locals => {:rules => relevant_rules } %> 47 | 48 |

All Rules

49 | <%= render :partial => 'rule_table', :locals => {:rules => @ability.instance_variable_get('@rules') } %> 50 | -------------------------------------------------------------------------------- /app/views/tuttle/devise/index.html.erb: -------------------------------------------------------------------------------- 1 |

Devise Configuration

2 | 3 | 10 | 11 |
12 |
13 | <% Devise.mappings.each do |key, mapping| %> 14 |
15 |
16 |

:<%= key %> => <%= mapping.class_name %>

17 |
18 |
19 |

Attributes

20 | 21 | 22 | 23 | 24 | 25 | <% [:singular, :scoped_path, :path, :controllers, :path_names, 26 | :class_name, :sign_out_via, :format, :used_routes, :used_helpers, 27 | :failure_app, :router_name, :strategies, :authenticatable?].each do |attr| %> 28 | 29 | 30 | 31 | 32 | <% end %> 33 |
AttributeValue
<%= attr %><%= mapping.send(attr) %>
34 |

Enabled Modules

35 | 36 | 37 | 38 | 39 | 40 | <% mapped_class = mapping.to 41 | mapped_class.devise_modules.each do |devise_module| %> 42 | 43 | 44 | 58 | 59 | <% end %> 60 |
Module NameConfig Options
:<%= devise_module %> 45 | <% 46 | mod = Devise::Models.const_get(devise_module.to_s.classify) 47 | if mod.const_defined?(:ClassMethods) 48 | class_mod = mod.const_get(:ClassMethods) 49 | if class_mod.respond_to?(:available_configs) 50 | class_mod.available_configs.each do |config| %> 51 | <%= config %>: 52 | <%= mapped_class.send(config) %>
53 | <% end 54 | end 55 | end 56 | %> 57 |
61 |
62 | <% end %> 63 |
64 |
65 |
66 |
67 |
Devise Version
<%= Devise::VERSION %>
68 |
69 |
 70 |   Devise::ALL = <%= Devise::ALL %>
 71 | 
 72 |   Devise::CONTROLLERS = <%= Devise::CONTROLLERS %>
 73 | 
 74 |   Devise::ROUTES = <%= Devise::ROUTES %>
 75 | 
 76 |   Devise::STRATEGIES = <%= Devise::STRATEGIES %>
 77 | 
 78 |   Devise::URL_HELPERS = <%= Devise::URL_HELPERS %>
 79 | 
 80 | 
81 |
82 |
83 |
 84 | rememberable_options = <%= Devise.rememberable_options %>
 85 | stretches = <%= Devise.stretches %>
 86 | http_authentication_key = <%= Devise.http_authentication_key %>
 87 | authentication_keys = <%= Devise.authentication_keys %>
 88 | request_keys = <%= Devise.request_keys %>
 89 | case_insensitive_keys = <%= Devise.case_insensitive_keys %>
 90 | strip_whitespace_keys = <%= Devise.strip_whitespace_keys %>
 91 | http_authenticatable = <%= Devise.http_authenticatable %>
 92 | http_authenticatable_on_xhr = <%= Devise.http_authenticatable_on_xhr %>
 93 | params_authenticatable = <%= Devise.params_authenticatable %>
 94 | http_authentication_realm = <%= Devise.http_authentication_realm %>
 95 | email_regexp = <%= Devise.email_regexp %>
 96 | password_length = <%= Devise.password_length %>
 97 | remember_for = <%= Devise.remember_for %>
 98 | extend_remember_period = <%= Devise.extend_remember_period %>
 99 | allow_unconfirmed_access_for = <%= Devise.allow_unconfirmed_access_for %>
100 | confirm_within = <%= Devise.confirm_within %>
101 | confirmation_keys = <%= Devise.confirmation_keys %>
102 | reconfirmable = <%= Devise.reconfirmable %>
103 | timeout_in = <%= Devise.timeout_in %>
104 | pepper = <%= Devise.pepper %>
105 | scoped_views = <%= Devise.scoped_views %>
106 | lock_strategy = <%= Devise.lock_strategy %>
107 | unlock_keys = <%= Devise.unlock_keys %>
108 | unlock_strategy = <%= Devise.unlock_strategy %>
109 | maximum_attempts = <%= Devise.maximum_attempts %>
110 | unlock_in = <%= Devise.unlock_in %>
111 | reset_password_keys = <%= Devise.reset_password_keys %>
112 | reset_password_within = <%= Devise.reset_password_within %>
113 | default_scope = <%= Devise.default_scope %>
114 | mailer_sender = <%= Devise.mailer_sender %>
115 | skip_session_storage = <%= Devise.skip_session_storage %>
116 | navigational_formats = <%= Devise.navigational_formats %>
117 | sign_out_all_scopes = <%= Devise.sign_out_all_scopes %>
118 | sign_out_via = <%= Devise.sign_out_via %>
119 | parent_controller = <%= Devise.parent_controller %>
120 | parent_mailer = <%= Devise.parent_mailer %>
121 | router_name = <%= Devise.router_name %>
122 | omniauth_path_prefix = <%= Devise.omniauth_path_prefix %>
123 | clean_up_csrf_token_on_authentication = <%= Devise.clean_up_csrf_token_on_authentication %>
124 | omniauth_configs = <%= Devise.omniauth_configs %>
125 | helpers = <%= Devise.helpers %>
126 | warden_config = <%= Devise.warden_config %>
127 | paranoid = <%= Devise.paranoid %>
128 | last_attempt_warning = <%= Devise.last_attempt_warning %>
129 | token_generator = <%= Devise.token_generator %>
130 | 
131 | omniauth_providers = <%= Devise.omniauth_providers %>
132 | available_router_name = <%= Devise.available_router_name %>
133 | 
134 |
135 | 136 |
137 | -------------------------------------------------------------------------------- /app/views/tuttle/execjs/index.html.erb: -------------------------------------------------------------------------------- 1 | 4 | 5 |

ExecJS

6 | 7 |

8 | ExecJS lets you run JavaScript code from Ruby. 9 |

10 |

11 | ExecJS automatically picks the best runtime available to evaluate your JavaScript program, then returns the result to you as a Ruby object. 12 |

13 |

14 | You can override the selection process by setting a EXECJS_RUNTIME environment variable. 15 |

16 |

17 | ExecJS is used by gems such as Uglifier and CoffeeScript even if you don't call evaluate JavaScript directly from your own application. 18 |

19 | 20 |
21 |
22 |

ExecJS Configuration

23 |
24 |
25 | 26 |
27 |
ExecJS Version
28 |
<%= ExecJS::VERSION %>
29 | 30 |
Autodetect: (Environment or best-available)
31 |
<%= ExecJS::Runtimes.autodetect.try(:name) %>
32 | 33 |
Best Available: (First non-deprecated, available runtime)
34 |
<%= ExecJS::Runtimes.best_available.name %>
35 | 36 |
From Environment
37 |
<%= ExecJS::Runtimes.from_environment.try(:name).inspect %>
38 | 39 |
ENV["EXECJS_RUNTIME"]: (must match Name in table below)
40 |
<%= ENV["EXECJS_RUNTIME"].inspect %>
41 | 42 |
Platform installed NodeJS (`nodejs --version || node --version`)
43 |
<%= `(nodejs --version || node --version) 2>/dev/null` %>
44 | 45 |
46 | 47 |

ExecJS Runtimes

48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | <% ExecJS::Runtimes.names.each do |runtime_name| %> 58 | <% runtime = ExecJS::Runtimes.const_get(runtime_name) %> 59 | <%= runtime == ExecJS::Runtimes.best_available ? "info best_available" : "" %>"> 60 | 61 | 62 | 67 | 79 | 80 | 81 | <% end %> 82 |
NameDescriptionSelectionDetailsDeprecated
<%= runtime_name %><%= runtime.name %> 63 | <%= true_label(runtime == ExecJS::Runtimes.autodetect, "auto-detected")%> 64 | <%= true_label(runtime == ExecJS::Runtimes.best_available, "best available")%> 65 | <%= true_label(runtime == ExecJS::Runtimes.from_environment, "from environment")%> 66 | 68 | <% if runtime.available? %> 69 |
70 | <%= runtime.class %> 71 |

72 | Command: <%= runtime.instance_variable_get(:@command).try(:inspect) %>
73 | Runner Path: <%= display_path(runtime.instance_variable_get(:@runner_path)) %>
74 | 75 |

76 |
77 | <% end %> 78 |
<%= truth_label(runtime.deprecated?) %>
83 | 84 |
85 | 87 |
88 | -------------------------------------------------------------------------------- /app/views/tuttle/gems/get_process_mem.html.erb: -------------------------------------------------------------------------------- 1 |

Process and Memory

2 | <% if defined?(GetProcessMem) %> 3 | GetProcessMem Version: <%= GetProcessMem::VERSION %> 4 | <% end %> 5 | 6 |

Ruby Process Information

7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
PID<%= Process.pid %>
Parent PPID<%= Process.ppid %>
Process EGID<%= Process.egid %>
Process EUID<%= Process.euid %>
Process pgrp<%= Process.getpgrp %>
Process rlimit(Process::RLIMIT_CORE)<%= Process.getrlimit(Process::RLIMIT_CORE) %>
Process rlimit(Process::RLIMIT_MEMLOCK)<%= Process.getrlimit(Process::RLIMIT_MEMLOCK) %>
Process getsid<%= Process.getsid %>
Process gid<%= Process.gid %>
Process groups<%= Process.groups %>
Process maxgroups<%= Process.maxgroups %>
53 | 54 | 55 | <% if @memory_self %> 56 |

GetProcessMem

57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | <% if @memory_self.respond_to?(:linux_status_memory) %> 71 | 72 | 73 | 74 | 75 | <% end %> 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | <% if @memory_parent.respond_to?(:linux_status_memory) %> 85 | 86 | 87 | 88 | 89 | <% end %> 90 |
Linux?<%= truth_label(@memory_self.linux?) %>
PID bytes<%= ActiveSupport::NumberHelper.number_to_human_size(@memory_self.bytes.to_i) %>
PID ps_memory<%= ActiveSupport::NumberHelper.number_to_human_size(@memory_self.send(:ps_memory).to_i) %>
PID status_memory<%= @memory_self.linux_status_memory > 0 ? ActiveSupport::NumberHelper.number_to_human_size(@memory_self.linux_status_memory.to_i) : '--Unknown--' %>
PPID bytes<%= ActiveSupport::NumberHelper.number_to_human_size(@memory_parent.bytes.to_i) %>
PPID ps_memory<%= ActiveSupport::NumberHelper.number_to_human_size(@memory_parent.send(:ps_memory).to_i) %>
PPID status_memory<%= @memory_parent.linux_status_memory > 0 ? ActiveSupport::NumberHelper.number_to_human_size(@memory_parent.linux_status_memory.to_i) : '--Unknown--' %>
91 | 92 | <% end %> 93 | -------------------------------------------------------------------------------- /app/views/tuttle/gems/http_clients.html.erb: -------------------------------------------------------------------------------- 1 |

HTTP Clients

2 | 3 |
4 |
5 |

HTTP Client Libraries

6 |
7 |
8 |

9 | HTTP client libraries are used to make requests to remote servers often as REST or SOAP requests. 10 | There are many libraries available with different features and purposes. 11 | Often client libraries are pulled in as dependencies of other gems you've included. 12 |

13 | 14 |

Libraries detected and loaded are marked in green

15 | 31 | 32 |
33 | 35 |
36 | -------------------------------------------------------------------------------- /app/views/tuttle/gems/index.html.erb: -------------------------------------------------------------------------------- 1 |

Gems <%= @gemspecs.size %>

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | <% @gemspecs.each do |gemspec| %> 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | <% end %> 20 |
NameVersionSummaryLicenseHomepage
<%= gemspec.name %><%= gemspec.version.to_s %><%= gemspec.summary %><%= gemspec.licenses.join(', ') %><%= link_to gemspec.homepage, gemspec.homepage unless gemspec.homepage.blank? %>
21 | -------------------------------------------------------------------------------- /app/views/tuttle/gems/json.html.erb: -------------------------------------------------------------------------------- 1 |

JSON Gems

2 | 3 |
4 |
5 |

JSON Libraries

6 |
7 |
8 |

9 | There are a small number of libraries that are involved in JSON encoding and decoding. 10 | Depending on the nature of the JSON you are decoding or objects you are encoding you may see a benefit by using the appropriate library. 11 |

12 | 13 |

Libraries detected and loaded are marked in green

14 |
    15 |
  • ActiveSupport::JSON
  • 16 |
  • JSON
  • 17 |
  • JSON Pure
  • 18 |
  • 19 | MultiJson 20 | <%- if defined?(MultiJson) %> 21 | — (Adapter: <%= MultiJson.adapter %>) 22 | <%- end %> 23 |
  • 24 |
  • Oj
  • 25 |
  • Yajl
  • 26 |
27 | 28 |
29 | 31 |
32 | -------------------------------------------------------------------------------- /app/views/tuttle/gems/other.html.erb: -------------------------------------------------------------------------------- 1 |

Other Gems

2 | 3 |
4 |
5 |

Performance Libraries

6 |
7 |
8 |

9 | Various gems are often included in applications to eke out a bit more performance. 10 | Here are various gems which may be helpful. 11 |

12 |

fast_blank - <%= truth_label("TEST".methods.include?(:blank_as?), 'installed', 'not installed') %>

13 |
14 | 16 |
17 | 18 |
19 |
20 |

Work In Progress

21 |
22 |
23 |

Much of Tuttle is a work in progress. But these bits are still under development...

24 |
    25 |
  • <%= link_to 'Request Inspector', '/tuttle/request' %>
  • 26 | <% if defined?(ActiveModel::Serializer) || defined?(ActiveModelSerializers) %> 27 |
  • <%= link_to 'ActiveModel Serializers', '/tuttle/active_model_serializers' %> - (0.9 and 0.10 support)
  • 28 | <% end %> 29 | <% if defined?(::Rack::MiniProfiler::ClientSettings) %> 30 |
  • <%= link_to 'Rack::MiniProfiler', rack_mini_profiler_path %>
  • 31 | <% end %> 32 |
33 |
34 | 36 |
37 | 38 | 39 |
40 |
41 |

Experimental Request Profiler

42 |
43 |
44 |

45 | Tuttle includes an optional middleware (Tuttle::Middleware::RequestProfiler) 46 | to assist with memory and CPU profiling using the 47 | memory_profiler and ruby-prof gems. 48 |

49 |

50 | This middleware provides the ability to profile individual requests on demand 51 | and to vary the profiling options. 52 |

53 |

Middleware Installation

54 |

55 | Middleware loaded? <%= truth_label(defined?(Tuttle::Middleware::RequestProfiler)) %> 56 |

57 | <% if defined?(Tuttle::Middleware::RequestProfiler) && middleware_position = Rails.application.config.middleware.middlewares.index(Tuttle::Middleware::RequestProfiler) %> 58 |

59 | Middleware stack position: <%= middleware_position + 1 %> 60 |

61 |
62 | 63 |

Memory Profiling <%= truth_label(defined?(MemoryProfiler), 'available', 'not available') %>

64 |

Profile memory allocation using memory_profiler with tuttle-profiler=memory_profiler query param.

65 |

66 | Additional parameters can include: 67 |

68 |
    69 |
  • memory_profiler_allow_files - Regexp pattern for files to be included
  • 70 |
  • memory_profiler_ignore_files - Regexp pattern for files to be ignored
  • 71 |
  • memory_profiler_top - Number of lines to include in report (defaults to 50)
  • 72 |
73 |

Example: 74 | <% example_url = main_app_root_url + '?tuttle-profiler=memory_profiler' %> 75 | <%= link_to example_url, example_url %> 76 |

77 |
78 | 79 |

CPU Profiling <%= truth_label(defined?(RubyProf), 'available', 'not available') %>

80 |

Profile CPU usage using ruby-prof with tuttle-profiler=ruby-prof query param.

81 |

82 | Specify the profile type with a ruby-prof_printer parameter: 83 |

    84 |
  • stack - (Default) Visualization of the call hierarchy with time spent in each method
  • 85 |
  • fast_stack - Fast version of stack (without colors or links)
  • 86 |
  • flat - Overall time spent in each method
  • 87 |
  • graph - Time spent in each method and callers/callees
  • 88 |
89 |

Example: 90 | <% example_url = main_app_root_url + '?tuttle-profiler=ruby-prof&ruby-prof_printer=fast_stack' %> 91 | <%= link_to example_url, example_url %> 92 |

93 | 94 | <% else %> 95 |

96 | You can include this middleware by configuring it in an initializer. 97 | It can be put at the beginning of the stack to help profile the other middleware. 98 | Or it can be put at the end of the stack to focus on memory from the application. 99 |

100 |
101 |     require 'tuttle/middleware/request_profiler'
102 |     # Add memory profiler middleware at beginning of the stack
103 |     # Rails.application.config.middleware.insert(0, Tuttle::Middleware::RequestProfiler)
104 | 
105 |     # Add memory profiler middleware at the end of the stack
106 |     Rails.application.config.middleware.use Tuttle::Middleware::RequestProfiler
107 |       
108 | <% end %> 109 | 110 |
111 |

112 | Note: Ruby-prof also provides a middleware that saves profile results to a directory 113 | on the server for every request. And RackMiniProfiler includes this same 114 | memory_profiler capability but requires RackMiniProfiler to be enabled, 115 | which has an impact on profiling. 116 |

117 |
118 | 120 |
121 | 122 |
123 |
124 |

Facter

125 |
126 |
127 |

128 | Facter is a gem that will collect and display system facts. 129 | It is typically used by Puppet separately from your application. 130 | If the gem is available, those facts will be reported here by Tuttle. 131 |

132 | 133 | <% if defined?(Facter) %> 134 |

Facter has collected the following information:

135 |
136 | <% Facter.to_hash.sort.each do |k, v| %> 137 |
<%= k %>
<%= v.inspect %>
138 | <% end %> 139 |
140 | <% else %> 141 |

142 | Facter is not available 143 |

144 |

You can optionally require this gem (even if you do not use Puppet) by adding the following to your Gemfile.

145 |
gem 'facter'
146 | <% end %> 147 |
148 |
149 | -------------------------------------------------------------------------------- /app/views/tuttle/home/index.html.erb: -------------------------------------------------------------------------------- 1 |

Tuttle Dashboard

2 | 3 |
4 |
5 | 6 |
7 |
8 |

Rails

9 |
10 |
11 |

Version: <%= Rails::VERSION::STRING %>

12 |

Environment: <%= Rails.env %>

13 |

Root: <%= Rails.root %>

14 |

Database: <%= ::ActiveRecord::Base.connection.adapter_name rescue 'N/A' %>

15 |

Caching: <%= truth_label(::ActionController::Base.send(:cache_configured?), 'Enabled', 'Disabled') %>

16 |
17 | 23 |
24 | 25 |
26 |
27 |

Libraries and Frameworks

28 |
29 |
    30 |
  • <%= link_to 'ActiveSupport', active_support_path %>
  • 31 | <%- if defined?(::ActiveJob) %> 32 |
  • <%= link_to 'ActiveJob', active_job_path %>
  • 33 | <%- end %> 34 | <%- if defined?(CanCanCan) %> 35 |
  • <%= link_to 'CanCanCan', cancancan_path %>
  • 36 | <%- end %> 37 | <%- if defined?(Devise) %> 38 |
  • <%= link_to 'Devise', devise_path %>
  • 39 | <%- end %> 40 |
  • <%= link_to 'HTTP Clients', gems_http_clients_path %>
  • 41 |
  • <%= link_to 'JSON', gems_json_path %>
  • 42 |
43 |
44 | 45 |
46 |
47 |

Performance Tuning

48 |
49 |
    50 |
  • <%= link_to 'Ruby VM tuning', ruby_tuning_path %>
  • 51 |
  • <%= link_to 'Request Profiling', gems_other_path(:anchor => 'request-profiler') %>
  • 52 |
53 |
54 | 55 |
56 |
57 |
58 |
59 |

Tuttle

60 |
61 |
62 |

Tuttle Version: <%= Tuttle::VERSION %>

63 |

Mode: <%= Rails.env %>

64 |

Session ID: <%= Tuttle::Engine.session_id %>

65 |

Uptime: <%= time_ago_in_words(Tuttle::Engine.session_start) %> (<%= Tuttle::Engine.session_start.to_s(:db) %>)

66 |
67 |
68 | 69 |
70 |
71 |

Ruby

72 |
73 |
74 |

Version: <%= RUBY_DESCRIPTION %>

75 |

Gems: <%= Bundler.rubygems.all_specs.size %>

76 |
77 | 81 |
82 | 83 |
84 | 85 |
86 | -------------------------------------------------------------------------------- /app/views/tuttle/i18n/_translation_entry.html.erb: -------------------------------------------------------------------------------- 1 | <% k, v = local_assigns[:translation_entry] %> 2 | <% if v.is_a? (Hash) %> 3 |
  • <%= k.inspect %> 4 |
      5 | <%= render partial: 'translation_entry', collection: v.to_a.sort_by { |entry| "#{entry[1].is_a?(Hash)}-#{entry[0]}" } %> 6 |
    7 |
  • 8 | <% else %> 9 |
  • <%= k.inspect %> = <%= v.inspect %>
  • 10 | <% end %> 11 | -------------------------------------------------------------------------------- /app/views/tuttle/i18n/index.html.erb: -------------------------------------------------------------------------------- 1 |

    Internationalization

    2 | 3 |

    4 | Rails Internationalization (I18n) configuration is described in the Rails 5 | Guide for the <%= link_to_rails_guide('i18n', 'Rails Internationalization API') %>. 6 |

    7 | 8 |
    9 |
    I18n Version
    <%= I18n::VERSION %>
    10 |
    Rails I18n loaded
    <%= truth_label(defined?(::RailsI18n)) %>
    11 | <% if defined?(::RailsI18n) %> 12 |
    Rails-I18n Version
    <%= Bundler.rubygems.loaded_specs('rails-i18n').version rescue "Unknown" %>
    13 | <% end %> 14 |
    15 | 16 |

    Config Settings

    17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
    SettingConfigDescription
    I18n.default_locale<%= I18n.default_locale.inspect %>Default locale
    I18n.default_separator<%= I18n.default_separator.inspect %>Default separator (should be .)
    I18n.exception_handler<%= I18n.exception_handler.class.inspect %>Exception handler (class)
    I18n.backend<%= I18n.backend.class.inspect %>Backend (class)
    I18n.available_locales<%= I18n.available_locales %>Available locales
    I18n.enforce_available_locales<%= truth_label(I18n.enforce_available_locales) %>Enforce available locales
    I18n::Locale::Tag.implementation<%= I18n::Locale::Tag.implementation %>Locale format support for fallbacks
    59 | 60 |

    i18n Extensions

    61 |

    Note: Fallbacks and Pluralization are enabled if the rails-i18n gem is loaded

    62 |

    Fallbacks <%= truth_label(I18n.respond_to?(:fallbacks), "enabled", "not enabled") %>

    63 | <% if I18n.respond_to?(:fallbacks) %> 64 | 65 | <% I18n.fallbacks.each do |k, v| %> 66 | 67 | 68 | 69 | 70 | <% end %> 71 |
    <%= k.inspect %><%= v.inspect %>
    72 | <% end %> 73 |

    Pluralization <%= truth_label(I18n.backend.class < I18n::Backend::Pluralization, "enabled", "not enabled") %>

    74 | <% if I18n.backend.respond_to?(:pluralize) %> 75 | 76 | 77 | 78 | 79 | 80 | <% @sorted_locales.each do |locale| %> 81 | 82 | 83 | 84 | 85 | <% end %> 86 |
    LocalePlural Keys
    <%= locale.inspect %><%= I18n.t('i18n.plural.keys', locale: locale) %>
    87 | <% end %> 88 | 89 |

    Cache <%= truth_label(I18n.respond_to?(:cache_store), "enabled", "not enabled") %>

    90 |

    GetText <%= truth_label(I18n.backend.class < I18n::Backend::Gettext, "enabled", "not enabled") %>

    91 | 92 | <% config_paths = Rails.application.config.i18n.load_path.map(&:to_s) %> 93 |

    I18n load path <%= I18n.load_path.size %> files

    94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | <% I18n.load_path.each do |path_location| %> 102 | 103 | 106 | 107 | 108 | 109 | 110 | <% end %> 111 |
    PathConfiguredExistsFile Size
    104 | <%= display_path(path_location) %> 105 | <%= truth_label(config_paths.include?(path_location)) %><%= truth_label(File.exist?(path_location)) %><%= File.size(path_location) %>
    112 | -------------------------------------------------------------------------------- /app/views/tuttle/i18n/localize.html.erb: -------------------------------------------------------------------------------- 1 | <% date_current = Date.current %> 2 | <% time_current = Time.current %> 3 |

    Localization

    4 |

    5 | Rails will localize Dates and Times using keys scoped under 6 | date.format and time.format. 7 | Typically the rails-i18n gem will provide the default 8 | translations for these translations but they can be overridden. 9 |

    10 |

    11 | The current date is <%= date_current.to_s %>
    12 | The current time is <%= time_current.to_s %>
    13 |

    14 | 15 |

    Dates

    16 | <% date_formats = I18n.translate('date.formats', locale: I18n.default_locale) %> 17 | 18 | 19 | 20 | <% date_formats.each do |format, _format_string| %> 21 | 22 | <% end %> 23 | 24 | <% @sorted_locales.each do |locale_name| %> 25 | <% locale_date_formats = I18n.translate('date.formats', locale: locale_name, fallback: false) rescue nil %> 26 | 27 | 28 | <% date_formats.each do |format, _format_string| %> 29 | 30 | <% end %> 31 | 32 | <% end %> 33 |
    Locale<%= format.inspect %>
    <%= locale_name %><%= l(date_current, locale: locale_name, format: format) rescue 'translation missing' %>
    34 | 35 |

    Times

    36 | <% time_formats = I18n.translate('time.formats', locale: I18n.default_locale) %> 37 | 38 | 39 | 40 | 41 | <% time_formats.each do |format, _format_string| %> 42 | 43 | <% end %> 44 | 45 | <% @sorted_locales.each do |locale_name| %> 46 | 47 | 48 | <% time_formats.each do |format, _format_string| %> 49 | 50 | <% end %> 51 | 52 | <% end %> 53 |
    Locale<%= format.inspect %>
    <%= locale_name %><%= l(time_current, locale: locale_name, format: format) rescue 'translation missing' %>
    54 | -------------------------------------------------------------------------------- /app/views/tuttle/i18n/translations.html.erb: -------------------------------------------------------------------------------- 1 |

    Translations for <%= I18n.locale.inspect %>

    2 | 3 |

    4 | Available locales: 5 | <% I18n.available_locales.sort.each do |hl| %> 6 | <%= link_to hl.inspect, i18n_translations_path(hl: hl), class:"btn btn-xs #{ I18n.locale == hl ? 'btn-success' : 'btn-default'}", role: "button" %> 7 | <% end %> 8 |

    9 | 10 | 13 | -------------------------------------------------------------------------------- /app/views/tuttle/rack_mini_profiler/index.html.erb: -------------------------------------------------------------------------------- 1 |

    Rack MiniProfiler

    2 | 3 |
    4 |
    MiniProfiler Version
    <%= Rack::MiniProfiler::VERSION %>
    5 |
    MiniProfiler Asset Version
    <%= Rack::MiniProfiler::ASSET_VERSION %>
    6 |
    7 | 8 |

    Rack::MiniProfiler Global Settings

    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | <% [:auto_inject, :disable_caching, :start_hidden, :skip_schema_queries, :enabled].each do |setting_method| %> 17 | 18 | 19 | 20 | 21 | 22 | <% end %> 23 | 24 | <% [:position, :authorization_mode, :toggle_shortcut, :flamegraph_sample_rate, 25 | :base_url_path, :backtrace_remove, :backtrace_includes, :backtrace_ignores, :backtrace_threshold_ms].each do |setting_method| %> 26 | 27 | 28 | 29 | 30 | 31 | <% end %> 32 | 33 | <% [ :storage, :storage_options, :storage_instance, :storage_failure, :user_provider, :pre_authorize_cb ].each do |setting_method| %> 34 | 35 | 36 | 37 | 38 | 39 | <% end %> 40 | 41 |
    SettingValueDescription
    Booleans
    <%= setting_method %><%= truth_label(@mp_config.send(setting_method)) %>
    Strings/Integers
    <%= setting_method %><%= @mp_config.send(setting_method).inspect %>
    Non-string
    <%= setting_method %><%= @mp_config.send(setting_method).inspect %>
    42 | 43 |

    Rack::MiniProfiler User Settings

    44 |

    45 | MiniProfiler uses a session cookie to store user overrides of configuration. 46 |

    47 |

    48 | This is controlled by using the pp=(enable|disable) 49 | or pp=(no|full|disable)-backtrace query parameters. 50 |

    51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
    SettingValueDescription
    <%= @mp_client_settings.tuttle_check_cookie_method %><%= truth_label(@mp_client_settings.tuttle_check_cookie) %>
    Cookie Value<%= request.cookies[::Rack::MiniProfiler::ClientSettings::COOKIE_NAME].inspect %>
    disable_profiling?<%= truth_label(@mp_client_settings.disable_profiling?) %>
    backtrace_level<%= @mp_client_settings.backtrace_level %>
    79 | 80 | -------------------------------------------------------------------------------- /app/views/tuttle/rails/_cache_dalli_store.html.erb: -------------------------------------------------------------------------------- 1 | <% 2 | # Copied from https://github.com/petergoldstein/dalli 3 | dalli_options = { 4 | expires_in: "Global default for key TTL. Default is 0, which means no expiry.", 5 | namespace: "If specified, prepends each key with this value to provide simple namespacing. Default is nil.", 6 | failover:" Boolean, if true Dalli will failover to another server if the main server for a key is down. Default is true.", 7 | threadsafe: "Boolean. If true Dalli ensures that only one thread is using a socket at a given time. Default is true. Set to false at your own peril.", 8 | serializer: "The serializer to use for objects being stored (ex. JSON). Default is Marshal.", 9 | compress: "Boolean, if true Dalli will gzip-compress values larger than 1K. Default is false.", 10 | compression_min_size: "Minimum value byte size for which to attempt compression. Default is 1K.", 11 | compression_max_size: "Maximum value byte size for which to attempt compression. Default is unlimited.", 12 | compressor: "The compressor to use for objects being stored. Default is zlib, implemented under Dalli::Compressor. If serving compressed data using nginx's HttpMemcachedModule, set memcached_gzip_flag 2 and use Dalli::GzipCompressor", 13 | keepalive: "Boolean. If true, Dalli will enable keep-alive for socket connections. Default is true.", 14 | socket_timeout: "Timeout for all socket operations (connect, read, write). Default is 0.5.", 15 | socket_max_failures: "When a socket operation fails after socket_timeout, the same operation is retried. This is to not immediately mark a server down when there's a very slight network problem. Default is 2.", 16 | socket_failure_delay: "Before retrying a socket operation, the process sleeps for this amount of time. Default is 0.01. Set to nil for no delay.", 17 | down_retry_delay: "When a server has been marked down due to many failures, the server will be checked again for being alive only after this amount of time. Don't set this value too low, otherwise each request which tries the failed server might hang for the maximum socket_timeout. Default is 1 second.", 18 | value_max_bytes: "The maximum size of a value in memcached. Defaults to 1MB, this can be increased with memcached's -I parameter. You must also configure Dalli to allow the larger size here.", 19 | error_when_over_max_size: "Boolean. If true, Dalli will throw a Dalli::ValueOverMaxSize exception when trying to store data larger than value_max_bytes. Defaults to false, meaning only a warning is logged.", 20 | username: "The username to use for authenticating this client instance against a SASL-enabled memcached server. Heroku users should not need to use this normally.", 21 | password: "The password to use for authenticating this client instance against a SASL-enabled memcached server. Heroku users should not need to use this normally.", 22 | sndbuf: "In bytes, set the socket SO_SNDBUF. Defaults to operating system default.", 23 | rcvbuf: "In bytes, set the socket SO_RCVBUF. Defaults to operating system default.", 24 | cache_nils: "Boolean. If true Dalli will not treat cached nil values as 'not found' for #fetch operations. Default is false.", 25 | } 26 | # TODO: more specifics on servers and pool (including connection_pool gem) 27 | %> 28 | 29 |

    Dalli/Memcache Options

    30 | 31 | 32 | 33 | 34 | 35 | 36 | <% dalli_options.each do |key, description| %> 37 | 38 | 39 | 40 | 41 | 42 | <% end %> 43 |
    SettingOption SettingDescription
    <%= key.inspect %><%= @cache.options[key].inspect %><%= description %>
    44 | 45 | <%- if @cache.respond_to?(:stats) %> 46 |

    MemCache Stats

    47 | 48 | 49 | 50 | 51 | 52 | <% @cache.stats.each do |server, stats| %> 53 | 54 | 55 | 56 | <% stats.each do |k, v| %> 57 | 58 | 59 | 60 | 61 | <% end %> 62 | <% end %> 63 |
    Stat
    Value
    Server: <%= server %>
    <%= k %><%= v %>
    64 | <%- end %> 65 | 66 | -------------------------------------------------------------------------------- /app/views/tuttle/rails/_cache_memory_store.html.erb: -------------------------------------------------------------------------------- 1 |

    Memory Store Internals

    2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
    SettingValueDescription
    Cache Size<%= @cache.instance_variable_get(:@cache_size) %>Size of the entries including keys and overhead
    Max Size<%= @cache.instance_variable_get(:@max_size) %>Maximum allowed size which triggers pruning. Default is 32MB.
    Max Prune Time<%= @cache.instance_variable_get(:@max_prune_time) %>Time limit for doing pruning
    Entries<%= @cache.instance_variable_get(:@data).size %>Total number of active entries
    29 | 30 | <% timenow = Time.now.to_f %> 31 |

    Memory Store keys by last access time (seconds ago)

    32 | 33 | 34 | 35 | 36 | 37 | <% @cache.instance_variable_get(:@key_access).sort_by(&:last).reverse!.each do |k, v| %> 38 | 39 | 40 | 41 | 42 | <% end %> 43 |
    Last Access
    Key
    <%= (timenow - v).round(2) %><%= k %>
    44 | -------------------------------------------------------------------------------- /app/views/tuttle/rails/_cache_monitor.html.erb: -------------------------------------------------------------------------------- 1 | <%- # TODO: Controls 2 | # Add options here for MemoryStore(clear, cleanup, and prune), FileStore(clear, cleanup), and MemCacheStore(clear). 3 | # For MemoryStore, provide method to dump keys and Entry attributes for very granular testing 4 | # For testing/inspection, provide a form to manipulate (add,remove,incr,decr) entries 5 | # TODO: Track calls and record keys broken down by namespace 6 | -%> 7 | 8 |

    Monitoring/Instrumentation

    9 | <%- unless Tuttle.track_notifications %> 10 | 13 | <%- end %> 14 | 15 |

    Recent cache interactions

    16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | <% @cache_events.reverse.each do |event| -%> 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | <% end -%> 37 |
    ActionKeyHit?OptionsTimeDurationTransactionID
    <%= event.name=='cache_read.active_support' ? 'read':'write' %><%= event.payload[:key] %><%= event.payload[:hit] %><%= event.payload.except(:key, :hit) %><%= event.time.to_s(:db) %><%= number_with_precision(event.duration) %><%= event.transaction_id %>
    38 | 39 |

    Recent cache call locations

    40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | <% @tuttle_cache_events.reverse.each do |event| -%> 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | <% end -%> 63 |
    ActionFileLineKeyHit?OptionsTimeTransactionID
    Read<%= event.payload[:call_location_path] %><%= event.payload[:call_location_lineno] %><%= event.payload[:key] %><%= event.payload[:hit] %><%= event.payload.reject {|k,_v| %i(key hit call_location_path call_location_lineno).include?(k) } %><%= event.time.to_s(:db) %><%= event.transaction_id %>
    64 | -------------------------------------------------------------------------------- /app/views/tuttle/rails/cache.html.erb: -------------------------------------------------------------------------------- 1 |

    2 | Caching <%= truth_label(ActionController::Base.send(:cache_configured?), 'Enabled', 'Disabled') %> 3 |

    4 | 5 |

    6 | For an in-depth overview of Rails Caching, see the 7 | Caching with Rails guide. 8 |

    9 | 10 |

    Configuration

    11 |

    Rails needs to have caching enabled and a specific cache implementation configured.

    12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | <% if ActionMailer::Base.respond_to?(:perform_caching=) %> 45 | 46 | 47 | 48 | 49 | 50 | <% end %> 51 |
    SettingValueDescription
    config.action_controller.perform_caching<%= truth_label(Rails.application.config.action_controller.perform_caching) %> 23 | Configures whether the application should perform caching or not. 24 | Typically set to false in development mode, true in production. 25 |
    config.cache_store<%= Rails.application.config.cache_store.inspect %> 31 | Configuration parameters that will be used to initialize the cache store. 32 |
    ENV['RAILS_CACHE_ID']<%= ENV['RAILS_CACHE_ID'].inspect %>Used to generate expanded cache keys in Rails' caching code if present
    ENV['RAILS_APP_VERSION']<%= ENV['RAILS_APP_VERSION'].inspect %>Used to generate expanded cache keys in Rails' caching code if present
    config.action_mailer.perform_caching<%= truth_label(Rails.application.config.action_mailer.perform_caching) %>
    52 | 53 | <% if Rails.application.config.action_controller.perform_caching %> 54 |

    Cache Details

    55 |

    56 | Rails.cache class = <%= @cache.class %>
    57 |

    58 |

    59 | Rails.cache options = <%= @cache.options.inspect %> 60 |

    61 |

    Rails.cache standard options:
    62 | <%- ActiveSupport::Cache::UNIVERSAL_OPTIONS.each do |option| %> 63 |   <%= option.inspect %> => <%= @cache.options[option].inspect %>
    64 | <%- end %> 65 |

    66 | 67 | <%- 68 | # Page and action caching were removed from Rails and made available in a separate gem 69 | # https://github.com/rails/actionpack-page_caching 70 | # https://github.com/rails/actionpack-action_caching 71 | # Page caching available? <%= !!defined?(ActionController::Caching::Pages) 72 | # Action caching available? <%= !!defined?(ActionController::Caching::Actions) 73 | -%> 74 | 75 | <%- if @cache.is_a?(ActiveSupport::Cache::NullStore) %> 76 |

    NullStore has no configuration options

    77 | <%- elsif @cache.is_a?(ActiveSupport::Cache::MemoryStore) %> 78 | <%= render partial: 'cache_memory_store' %> 79 | <%- elsif @cache.is_a?(ActiveSupport::Cache::MemCacheStore) || 80 | defined?(::ActiveSupport::Cache::DalliStore) && @cache.is_a?(ActiveSupport::Cache::DalliStore) %> 81 | <%= render partial: 'cache_dalli_store' %> 82 | <%- elsif @cache.is_a?(ActiveSupport::Cache::FileStore) %> 83 |

    84 | Rails FileStore options
    85 |   :cache_path => <%= @cache.cache_path %> 86 |

    87 | <%- else %> 88 |

    Unknown cache store.

    89 | <%- end %> 90 | 91 | <% # DISABLED render partial: 'cache_monitor' %> 92 | 93 | <% else %> 94 |

    95 | Rails is not configured to perform caching in this environment. 96 | You will need to set config.action_controller.perform_caching = true to enable caching. 97 |

    98 | <% end %> 99 | -------------------------------------------------------------------------------- /app/views/tuttle/rails/controllers.html.erb: -------------------------------------------------------------------------------- 1 |

    Controllers <%= @controllers.size %> and action methods <%= @controllers.map(&:action_methods).map(&:size).sum %>

    2 | 3 | <% @controllers.each do |controller| %> 4 | <%- controller_instance = controller.new %> 5 | <%- controller_path = controller.controller_path %> 6 | <%- callback_chain_presenter = Tuttle::Presenters::ActiveSupport::Callbacks::CallbackChainWrapper.new(controller._process_action_callbacks) %> 7 | 8 |

    <%= controller.inspect %> (<%= controller.controller_path %>) < <%= controller.superclass %>

    9 | 10 |
    11 | Callbacks <%= callback_chain_presenter.size %> 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | <% # TODO: show if/unless details %> 22 | <%- callback_chain_presenter.each do |cb| %> 23 | 24 | 25 | 26 | 27 | 28 | <% end %> 29 | 30 |
    NameKindLocation
    <%= cb.filter.inspect %><%= cb.kind.inspect %><%= display_source_locaction(*cb.safe_source_location(controller_instance)) %>
    31 |
    32 | 33 | <%- action_methods = controller.action_methods %> 34 |
    Actions <%= action_methods.size %>
    35 | 36 | 37 | <%- action_methods.each do |method| %> 38 | <% # TODO controller and action layout, template exists? %> 39 | <% # TODO routes to actions %> 40 | 41 | 42 | 52 | 53 | <% end %> 54 | 55 |
    <%= method.to_s %> 43 | <%- route_matches = @routes.select { |r| r.controller == controller_path && r.action == method.to_s } %> 44 | <% if route_matches.size > 0 %> 45 | <% route_matches.each do |route| %> 46 | <%= link_to "#{ route.verb } #{ route.path }", rails_routes_path(anchor: "route_#{route.precedence}") %>
    47 | <% end %> 48 | <% else %> 49 | <%= truth_label(false, nil, 'unrouted') %> 50 | <% end %> 51 |
    56 | <% end %> 57 | 58 | -------------------------------------------------------------------------------- /app/views/tuttle/rails/database.html.erb: -------------------------------------------------------------------------------- 1 |

    Database

    2 | 3 |

    Connection Configuration

    4 |
    5 | <%- ActiveRecord::Base.connection_config.each do |key,val| -%> 6 |
    <%= key %>
    <%= 'password'==key.to_s ? '--FILTERED--' : val %>
    7 | <%- end -%> 8 |
    9 | 10 |

    Current Connection

    11 |

    ActiveRecord::Migrator.current_version = <%= ActiveRecord::Migrator.current_version %>

    12 |

    Adapter Name = <%= @conn.adapter_name %>

    13 |

    Adapter Class = <%= @conn.class %>

    14 |

    Connected? = <%= ActiveRecord::Base.connected? %>

    15 |

    Pool size = <%= ActiveRecord::Base.connection_pool.size rescue 'N/A' %>

    16 |

    Schema Cache size = <%= @conn.schema_cache.size rescue 'N/A' %>

    17 | 18 | <%- if defined?(PG::Connection) && ActiveRecord::Base.connection.raw_connection.class <= PG::Connection -%> 19 |

    PostgreSQL Prepared Statements

    20 | 21 | <% statements = ActiveRecord::Base.connection.raw_connection.exec("select * from pg_prepared_statements order by prepare_time;").to_a %> 22 | <% statements.each do |row| %> 23 | 24 | 25 | 26 | 27 | <% end %> 28 |
    <%= row["name"] %><%= row["statement"] %>
    29 | <% end %> 30 | 31 | <%- if defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) && @conn.class <= ActiveRecord::ConnectionAdapters::PostgreSQLAdapter -%> 32 | 33 | <%- statement_pool = @conn.instance_variable_get(:@statements) %> 34 | <%- if statement_pool.present? %> 35 |

    Prepared Statement Pool

    36 |

    Max = <%= statement_pool.instance_variable_get(:@max) %>

    37 |

    Counter = <%= statement_pool.instance_variable_get(:@counter) %>

    38 |

    Cache

    39 |
      40 | <%- statement_pool.instance_variable_get(:@cache).each do |pid,cache| %> 41 |
    1. PID: <%= pid %> 42 |
        43 | <%- cache.each do |k,v| %> 44 |
      1. <%= v %> = <%= k %>
      2. 45 | <%- end %> 46 |
      47 |
    2. 48 | <%- end %> 49 |
    50 | <%- end %> 51 | 52 | 53 |

    PostgreSQL-only Info

    54 |

    Database = <%= @conn.current_database %>

    55 |

    Schema = <%= @conn.current_schema %>

    56 |

    Encoding = <%= @conn.encoding %>

    57 |

    Collation = <%= @conn.collation %>

    58 |

    Ctype = <%= @conn.ctype %>

    59 | 60 |

    Extensions

    61 | 69 | <%- end -%> 70 | 71 |

    Tables (Data Sources) <%= @data_sources.size %>

    72 |

    <%= @data_sources %>

    73 | 74 |

    Native DB Types Supported

    75 |
    76 | <%- @conn.native_database_types.each do |key,val| -%> 77 |
    <%= key %>
    <%= val %>
    78 | <%- end -%> 79 |
    80 | 81 | -------------------------------------------------------------------------------- /app/views/tuttle/rails/engines.html.erb: -------------------------------------------------------------------------------- 1 |

    Rails Engines

    2 | 3 |

    4 | Rails Engines are mini-applications that provide functionality to the host application. 5 | An Engine is like a plugin module that can expose controllers, assets, rake tasks, etc. 6 |

    7 | 8 | <%- @engines.each do |engine| %> 9 |
    10 |
    11 |

    <%= engine.engine_name %> - <%= engine.class.name %> <%= 'ISOLATED'.html_safe if engine.isolated? %>

    12 |
    13 |
    14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
    Engine root path<%= display_path(engine.root.to_s) %>
    Paths 22 | <% engine.paths.all_paths.each do |p| %> 23 | <% current_path = p.instance_variable_get(:@current) %> 24 | <% next if engine.paths[current_path].existent.empty? %> 25 | <%= current_path %>
    26 | <% end %> 27 |
    Routes<%= engine.routes.set.to_a.size %>
    Helpers<%= engine.helpers.instance_methods %>
    38 |
    39 |
    40 | <%- end %> 41 | -------------------------------------------------------------------------------- /app/views/tuttle/rails/generators.html.erb: -------------------------------------------------------------------------------- 1 |

    Rails Generators

    2 | 3 |

    Rails Generators are helpers for quickly creating new code that follows Rails standards and best practices.

    4 |

    Rails itself provides many generators as do other gems.

    5 |

    You can configure Rails Generators to generate files based on your project's preferences.

    6 |

    Configuration Settings

    7 |
    8 |
    assets = <%= Rails::Generators.options[:rails][:assets] %>
    9 |
    allows to create assets on generating a scaffold. Defaults to true.
    10 |
    force_plural = <%= Rails::Generators.options[:rails][:force_plural] %>
    11 |
    allows pluralized model names. Defaults to false.
    12 |
    helper = <%= Rails::Generators.options[:rails][:helper] %>
    13 |
    defines whether or not to generate helpers. Defaults to true.
    14 |
    integration_tool = <%= Rails::Generators.options[:rails][:integration_tool] %>
    15 |
    defines which integration tool to use. Defaults to nil.
    16 |
    javascripts = <%= Rails::Generators.options[:rails][:javascripts] %>
    17 |
    turns on the hook for JavaScript files in generators. Used in Rails for when the scaffold generator is run. Defaults to true.
    18 |
    javascript_engine = <%= Rails::Generators.options[:rails][:javascript_engine] %>
    19 |
    configures the engine to be used (for eg. coffee) when generating assets. Defaults to nil.
    20 | 21 |
    orm = <%= Rails::Generators.options[:rails][:orm] %>
    22 |
    defines which orm to use. Defaults to false and will use Active Record by default.
    23 |
    resource_controller = <%= Rails::Generators.options[:rails][:resource_controller] %>
    24 |
    defines which generator to use for generating a controller when using rails generate resource. Defaults to :controller.
    25 |
    scaffold_controller = <%= Rails::Generators.options[:rails][:scaffold_controller] %>
    26 |
    different from resource_controller, defines which generator to use for generating a scaffolded controller when using rails generate scaffold. Defaults to :scaffold_controller.
    27 |
    stylesheets = <%= Rails::Generators.options[:rails][:stylesheets] %>
    28 |
    turns on the hook for stylesheets in generators. Used in Rails for when the scaffold generator is run, but this hook can be used in other generates as well. Defaults to true.
    29 |
    stylesheet_engine = <%= Rails::Generators.options[:rails][:stylesheet_engine] %>
    30 |
    configures the stylesheet engine (for eg. sass) to be used when generating assets. Defaults to :css.
    31 |
    test_framework = <%= Rails::Generators.options[:rails][:test_framework] %>
    32 |
    defines which test framework to use. Defaults to false and will use Test::Unit by default.
    33 |
    template_engine = <%= Rails::Generators.options[:rails][:template_engine] %>
    34 |
    defines which template engine to use, such as ERB or Haml. Defaults to :erb.
    35 |
    36 |

    Generators

    37 |

    38 | Note: Generators marked as HIDDEN 39 | are typically invoked indirectly as a dependency of another generator. 40 |

    41 | 42 | <% @generators.each do |namespace, generators| %> 43 |
    44 |
    45 |

    <%= namespace %>

    46 |
    47 |
    48 | <% generators.each do |generator| %> 49 |
    50 | <%= generator.namespace %> 51 | <% if Rails::Generators.hidden_namespaces.include?(generator.namespace) %> 52 | HIDDEN 53 | <% end %> 54 |
    55 |

    Usage: <%= generator.banner %>

    56 |
    <%= generator.desc %>
    57 |
    58 | <% end %> 59 |
    60 |
    61 | <% end %> 62 | 63 |
    64 | 65 | 74 | 75 | -------------------------------------------------------------------------------- /app/views/tuttle/rails/helpers.html.erb: -------------------------------------------------------------------------------- 1 |

    Helpers <%= @helpers.size %>

    2 | 3 | helpers_path: <%= ::ApplicationController.helpers_path %>
    4 | include_all_helpers: <%= ::ApplicationController.include_all_helpers %>
    5 | 6 |
     7 | <% @helpers.each do |helper| %>
     8 | <%= helper.inspect %>
     9 | <%- helper.instance_methods(false).sort.each do |method| %>
    10 |   <%= method.to_s %>
    11 | <% end %>
    12 | <% end %>
    13 | 
    14 | 
    15 | 16 |

    Rails Helpers

    17 |
    18 | <%- ActionView::Helpers.instance_methods(true).sort.each do |method| %>
    19 |   <%= method.to_s %>
    20 | <% end %>
    21 | 
    22 | 
    23 | 24 |

    Rails Helpers by Module

    25 |
    26 | <%- ActionView::Helpers.included_modules.each do |module_obj| %>
    27 | <%= module_obj.inspect %>
    28 |   <%- module_obj.public_instance_methods(false).sort.each do |method| %>
    29 |   <%= method.to_s %>
    30 |   <%- # TODO: print method signatures -%>
    31 |   <%- # ActionView::Helpers.instance_method(method).parameters %>
    32 | <% end %>
    33 | <% end %>
    34 | 
    35 | 
    36 | -------------------------------------------------------------------------------- /app/views/tuttle/rails/instrumentation.html.erb: -------------------------------------------------------------------------------- 1 |

    Instrumentation

    2 | 3 | <%- unless Tuttle.track_notifications %> 4 | 7 | <%- end %> 8 |

    Events (<%= @events.size %>)

    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | <% @events.reverse.each do |event| -%> 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | <% end -%> 27 |
    NameTimeDurationPayloadTransactionID
    <%= event.name %><%= event.time.to_s(:db) %><%= number_with_precision(event.duration) %><%= truncate(event.payload.to_s, length: 250) rescue 'Payload not captured' %><%= event.transaction_id %>
    28 | -------------------------------------------------------------------------------- /app/views/tuttle/rails/models.html.erb: -------------------------------------------------------------------------------- 1 | 5 | 6 |

    Models <%= @models.size %>

    7 | 8 | <% if 0 == @models.size %> 9 |

    No models were found in this application

    10 | <% else %> 11 | <% @models.each do |model| %> 12 | 13 |
    14 |
    15 |

    <%= model.name %>

    16 |
    17 |
    18 |

    Associations <%= model.reflect_on_all_associations.size %>

    19 | <% if model.reflect_on_all_associations.size > 0 %> 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | <% model.reflect_on_all_associations.each do |reflection| %> 37 | <% reflection_presenter = ::Tuttle::Presenters::ActiveRecord::ReflectionPresenter.new(reflection, self) %> 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | <% end %> 54 |
    NameMacroTypeInverse OfScoped?Polymorphic?Validate?DependentClass NameAutosaveRequired?Foreign KeyOptions
    <%= reflection_presenter.name %><%= reflection_presenter.macro %><%= reflection_presenter.type %><%= reflection_presenter.inverse_of %><%= reflection_presenter.scoped? %><%= reflection_presenter.polymorphic? %><%= reflection_presenter.validate? %><%= reflection_presenter.options_dependent %><%= reflection_presenter.options_class_name %><%= reflection_presenter.options_autosave %><%= reflection_presenter.options_required %><%= reflection_presenter.foreign_key %><%= reflection_presenter.options_other %>
    55 | <% end %> 56 | 57 |

    Columns <%= model.columns.size %>

    58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | <% model.columns.each do |col| %> 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | <% end %> 81 |
    NameTypeDefaultNullLimitSQL TypePrecisionScale
    <%= col.name || Rails.configuration.filtered_parameters %><%= col.type %><%= col.default %><%= 'false' unless col.null %><%= col.limit %><%= col.sql_type %><%= col.precision %><%= col.scale %>
    82 | 83 |

    Validators: <%= model.validators.size %>

    84 | <% if model.validators.size > 0 %> 85 | 86 | 87 | 88 | 89 | 90 | 91 | <% model.validators.each do |validator| %> 92 | 93 | 94 | 95 | 96 | 97 | <% end %> 98 |
    KindAttributesOptions
    <%= validator.kind %><%= validator.attributes if validator.respond_to?(:attributes) %><%= validator.options %>
    99 | <% end %> 100 | 101 |

    Stored attributes <%= model.stored_attributes.size %>

    102 | <% if model.stored_attributes.size > 0 %> 103 | 104 | 105 | 106 | 107 | 108 | <% model.stored_attributes.each do |store, attributes| %> 109 | 110 | 111 | 112 | 113 | <% end %> 114 |
    Store NameAttributes
    <%= store.inspect %><%= attributes.inspect %>
    115 | <% end %> 116 | 117 |

    Callbacks Counts

    118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | <% [:save, :create, :validate, :commit, :touch, :rollback, :find, :initialize].each do |callback_type| %> 126 | <% callbacks = model.send(:get_callbacks, callback_type) %> 127 | 128 | 129 | 130 | 131 | 132 | 133 | <% end %> 134 |
    CallbackBeforeAfterAround
    <%= callback_type.inspect %><%= callbacks.select{ |cb| cb.kind.eql?(:before) }.size %><%= callbacks.select{ |cb| cb.kind.eql?(:after) }.size %><%= callbacks.select{ |cb| cb.kind.eql?(:around) }.size %>
    135 | 136 | <% if model.respond_to?(:find_by_statement_cache) %> 137 |

    FindBy Statement Cache <%= model.find_by_statement_cache.size %>

    138 | <% if model.find_by_statement_cache.size > 0 %> 139 | 140 | 141 | 142 | 143 | 144 | <% model.find_by_statement_cache.each do |keys, statement_cache| %> 145 | 146 | 147 | 148 | 149 | <% end %> 150 |
    KeysSQL
    <%= keys.inspect %><%= statement_cache.query_builder.instance_variable_get(:@sql) rescue '--Error--' %>
    151 | <% end %> 152 | <% end %> 153 | 154 |
    155 |
    156 | 157 | <% end %> 158 | 159 | <% end %> 160 | -------------------------------------------------------------------------------- /app/views/tuttle/rails/routes.html.erb: -------------------------------------------------------------------------------- 1 | <%- content_for(:javascripts) do -%> 2 | <%= javascript_tag do %> 3 | $(function () { $('[data-toggle="tooltip"]').tooltip(); }); 4 | <% end -%> 5 | <%- end %> 6 | 9 |

    Routes

    10 | 11 | 15 |
    16 | 17 |
    18 |
    19 |

    20 | Rails routing is described in the Rails 21 | Routing from the Outside In guide. 22 |

    23 |

    24 | In addition to the routes defined in your config/routes.rb file, 25 | Rails has internal routes and other Engines can contribute routes to the application. 26 |

    27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | <% @routes.each do |route| %> 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | <% end %> 57 |
    PrecedenceVerbPathNameEndpoint or AppConstraintsControllerActionInternal?Engine?Route valid?
    <%= route.precedence %><%= route.verb %><%= route.path %><%= route.name %><%= route.endpoint_or_app_name %><%= route.constraints %><%= route.controller %><%= route.action %><%= truth_label(route.internal?) %><%= truth_label(route.engine?) %><%= truth_label(route.route_problem.nil?) %> <%= route.route_problem %>
    58 |
    59 | 60 |
    61 |

    Paste a URL from your application to quickly get the corresponding controller/action

    62 |
    63 |
    64 | 65 |
    66 | 67 |
    68 |
    69 |
    70 |
    71 | 72 |
    73 |
    74 |
    75 | <% unless @recognized_paths.blank? %> 76 |
    77 |
    78 | 79 | <% @recognized_paths.each do |key, value| %> 80 | <% value.each do |route| %> 81 | 82 | 83 | 85 | <% end %> 86 | <% end %> 87 |
    <%= key.to_s.upcase %><%= route[:error] ? route[:error] : route %> 84 |
    88 |
    89 |
    90 | <% end %> 91 |
    92 | 93 |
    94 |
    95 | 96 | -------------------------------------------------------------------------------- /app/views/tuttle/rails/schema_cache.html.erb: -------------------------------------------------------------------------------- 1 |

    Database Schema Cache

    2 | 3 |

    4 | ActiveRecord maintains a cache of table, column, and primary key information 5 | that it uses to build the SQL for queries. 6 |

    7 | 8 |

    9 | This cache of information can either be built lazily as models are accessed 10 | or can be loaded from a dump file during application startup. 11 |

    12 | 13 |

    14 | Preloading the schema cache from the dump can greatly improve application 15 | performance, especially with a large number of tables. 16 |

    17 | 18 |

    Schema Cache Dump

    19 |

    20 | The schema_cache.dump file is created using rake db:schema:cache:dump. 21 | The dump file must match the version of the latest migration that has been run. 22 |

    23 | 30 | 31 |

    Current Schema Cache

    32 |

    33 | The current schema cache includes any table definitions that have been loaded from the cache dump or lazily evaluated as queries are built. 34 | The names of all tables are loaded very early. Primary keys, column names, or full column definitions are only loaded as the query builder requires them. 35 |

    36 |

    37 | The schema cache contains <%= @connection_schema_cache.size %> entries and 38 | <%= truth_label(@connection_schema_cache.version.present?,'DOES','DOES NOT') %> 39 | appear to have been loaded from the schema cache dump. 40 |

    41 | <% unless Rails.application.config.cache_classes %> 42 |

    Note: The schema cache is only loaded once on application startup. During development, with code reloading, it is likely to be reset.

    43 | <% end %> 44 | 45 | <%- # Rails 5.x switched to @data_sources from @tables for instance variable %> 46 | <% cached_tablenames = @connection_schema_cache.instance_variable_defined?(:@data_sources) && 47 | @connection_schema_cache.instance_variable_get(:@data_sources) || 48 | @connection_schema_cache.instance_variable_get(:@tables) || [] %> 49 | <% cached_columns = @connection_schema_cache.instance_variable_get(:@columns) %> 50 | <% cached_columns_hash = @connection_schema_cache.instance_variable_get(:@columns_hash) %> 51 | <% cached_primary_keys = @connection_schema_cache.instance_variable_get(:@primary_keys) %> 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | <% cached_tablenames.each do |key, value|%> 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | <% end %> 74 | <% if cached_tablenames.blank? %> 75 | 76 | 82 | 83 | <% end %> 84 |
    Table NameTable Exists?Primary KeyColumns SizeColumns Hash SizePartial?Full?
    <%= key %><%= truth_label(value) %><%= cached_primary_keys.fetch(key,'--'.freeze) %><%= cached_columns[key].try(:size) || '--'.freeze %><%= cached_columns_hash[key].try(:size) || '--'.freeze %><%= truth_label(cached_primary_keys.include?(key)||cached_columns.include?(key)||cached_columns_hash.include?(key)) %><%= truth_label(cached_primary_keys.include?(key)&&cached_columns.include?(key)&&cached_columns_hash.include?(key)) %>
    77 | No data exists in the schema cache 78 |
    79 | This most likely means that the connection has been reset either by 80 | code reloading or some connection problem 81 |
    85 | -------------------------------------------------------------------------------- /app/views/tuttle/request/index.html.erb: -------------------------------------------------------------------------------- 1 |

    Request Info

    2 | 3 |

    Session Variables

    4 | <% # TODO: 5 | # show session store configuration (class and options) 6 | # total session size 7 | # session variable actions - unset, set(string), new(key, string) 8 | # Cookie header string size, cookie count 9 | # cookie actions (client and/or server) - clear, update, new(with paths, secure, etc.) 10 | # Do secure cookies get accessed differently? 11 | %> 12 | 13 | 14 | 15 | 16 | 17 | <% @session_hash.each do |key, value| %> 18 | 19 | 20 | 21 | 22 | <% end %> 23 |
    KeyValue
    <%= key %><%= value %>
    24 | 25 |

    Cookies

    26 | 27 | 28 | 29 | 30 | 31 | <% @cookies_hash.each do |key, value| %> 32 | 33 | 34 | 35 | 36 | <% end %> 37 |
    KeyValue
    <%= key %><%= value %>
    38 | -------------------------------------------------------------------------------- /app/views/tuttle/ruby/constants.html.erb: -------------------------------------------------------------------------------- 1 |

    Constants

    2 | 3 |

    Global constants <%= @constants.size %>

    4 |

    5 | Global constants are variables that are defined outside of a module and start with a capital letter. 6 | In most cases, constants should be frozen to prevent them from being modified. 7 |

    8 |

    9 | Classes and modules are also constants. This table shows the non-class/module constants. 10 |

    11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | <% @constants.each do |name, klass, value| %> 22 | 23 | 24 | 25 | 26 | 27 | <% end %> 28 | 29 |
    ConstantFrozen?Class
    <%= name.inspect %><%= truth_label(value.frozen?) %><%= klass %>
    30 | -------------------------------------------------------------------------------- /app/views/tuttle/ruby/extensions.html.erb: -------------------------------------------------------------------------------- 1 |

    Extensions

    2 | 3 |

    Object Extensions <%= @obj_extensions.size %>

    4 |

    5 | Ruby's Object class can be extended by your own code or libraries you include. 6 | Here are all the methods on Object and where they were defined. 7 |

    8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | <% @obj_extensions.each do |meth, file, lineno| %> 18 | 19 | 20 | 21 | 22 | <% end %> 23 | 24 |
    MethodSource Location
    <%= meth.inspect %><%= display_source_locaction(file, lineno) %>
    25 | -------------------------------------------------------------------------------- /app/views/tuttle/ruby/index.html.erb: -------------------------------------------------------------------------------- 1 |

    Ruby Configuration

    2 | 3 | 11 | 12 |
    13 |
    14 |
    15 |
    RUBY_ENGINE
    <%= RUBY_ENGINE %>
    16 |
    RUBY_VERSION
    <%= RUBY_VERSION %>
    17 |
    RUBY_PATCHLEVEL
    <%= RUBY_PATCHLEVEL %>
    18 |
    RUBY_RELEASE_DATE
    <%= RUBY_RELEASE_DATE %>
    19 |
    RUBY_REVISION
    <%= RUBY_REVISION %>
    20 |
    RUBY_PLATFORM
    <%= RUBY_PLATFORM %>
    21 |
    RUBY_DESCRIPTION
    <%= RUBY_DESCRIPTION %>
    22 |
    RUBY_COPYRIGHT
    <%= RUBY_COPYRIGHT %>
    23 |
    24 |

    RUBYOPT

    25 |

    The RUBYOPT environment variable can be set pass command line options to Ruby.

    26 |
    <%= ENV.fetch('RUBYOPT') { 'Not set'} %>
    27 | 28 |

    RubyVM::DEFAULT_PARAMS

    29 |

    The Ruby VM default parameters are the default values for this version of Ruby. They are not configurable.

    30 | 31 | 32 | 33 | 34 | 35 | <% RubyVM::DEFAULT_PARAMS.each do |k, v| %> 36 | 37 | 38 | 39 | 40 | <% end %> 41 |
    SettingValue
    <%= k.inspect %><%= v %>
    42 | 43 |

    RubyVM::OPTS

    44 |

    The RubyVM OPTS are build-time settings specified when Ruby is compiled.

    45 |
    <%= RubyVM::OPTS.inspect %>
    46 |
    47 | 48 | 49 |
    50 |

    RbConfig

    51 |

    RbConfig records the compile-time settings that were used to build ruby.

    52 | 53 |

    54 | RbConfig.ruby = <%= RbConfig.ruby -%> 55 |
    56 | RbConfig::TOPDIR = <%= RbConfig::TOPDIR -%> 57 |

    58 | 59 |

    RbConfig::CONFIG

    60 | 61 | 62 | 63 | 64 | 65 | 66 | <% RbConfig::CONFIG.each do |k, v| %> 67 | 68 | 69 | 70 | 71 | 72 | <% end %> 73 |
    SettingValueUnexpanded Value
    <%= k %><%= v %><%= RbConfig::MAKEFILE_CONFIG[k] %>
    74 |
    75 | 76 |
    77 |

    78 | The ENV includes the environment variables. 79 |

    80 |
     81 | <% @filtered_env.each do |key, val| -%>
     82 | <%= key %> = <%= val %>
     83 | <% end -%>
     84 |     
    85 |
    86 | 87 |
    88 | 89 |

    Configuration

    90 |
    91 |
    Version
    <%= ::Bundler::VERSION %>
    92 |
    Local config file
    <%= Bundler.settings.send(:local_config_file) %>
    93 |
    Global config file
    <%= Bundler.settings.send(:global_config_file) %>
    94 |
    Require sudo?
    <%= truth_label(Bundler.requires_sudo?) %>
    95 |
    Allow sudo?
    <%= truth_label(Bundler.settings.allow_sudo?) %>
    96 |
    97 |

    Settings

    98 | 99 | 100 | 101 | 102 | 103 | 104 | <% Bundler.settings.all.reject {|key| key.start_with?('#')}.each do |key| %> 105 | 106 | 107 | 108 | 109 | 110 | <% end %> 111 |
    SettingValueConfigured by
    <%= key %><%= Bundler.settings[key].inspect %><%= Bundler.settings.pretty_values_for(key)[0] %>
    112 |
    113 | 114 |
    115 |

    116 | The $LOAD_PATH lists 117 | the directories that will be searched when code is loaded with 118 | require or load. 119 |

    120 |
      121 | <%- $LOAD_PATH.each do |dir_name| %> 122 |
    • <%= dir_name %>
    • 123 | <%- end %> 124 |
    125 |
    126 | 127 |
    128 |

    The following files have been loaded via require at some point.

    129 |
      130 | <%- $LOADED_FEATURES.each do |filename| %> 131 |
    • <%= filename %>
    • 132 | <%- end %> 133 |
    134 |
    135 | 136 |
    137 | -------------------------------------------------------------------------------- /app/views/tuttle/ruby/miscellaneous.html.erb: -------------------------------------------------------------------------------- 1 |

    Miscellaneous

    2 | 3 |

    Reslov-Replace

    4 |

    5 | By default, Ruby network libraries use the system DNS lookup. 6 | But the Ruby standard library also ships with a pure-Ruby implementation that causes the VM to not 7 | lock while waiting for DNS lookups to happen. To enable this lookup just add require 'resolv-replace' 8 | somewhere in your application, such as in an initializer. 9 |

    10 |

    11 | Does resolv-replace appear to be patched in? <%= truth_label(IPSocket.method(:getaddress).source_location.present?) %> 12 |

    13 | -------------------------------------------------------------------------------- /app/views/tuttle/ruby/tuning.html.erb: -------------------------------------------------------------------------------- 1 |

    Ruby Tuning

    2 | 3 |

    4 | Ruby can be tuned for different types of applications through the use of environment variables. 5 |

    6 |

    7 | Different versions of Ruby support different variables so it is important to make sure that you are using 8 | settings and environment variable names appropriate for your version of Ruby. 9 |

    10 | 11 |

    Global Method Cache

    12 |

    13 | Since Ruby 2.2, the method cache size is configurable using the RUBY_GLOBAL_METHOD_CACHE_SIZE environment variable. 14 |

    15 |

    16 | The default value for this setting is 2048 entries which may be low for even a moderate-sized Rails application. 17 |

    18 | <%- # TODO: warn if value is not a power of 2 %> 19 |
     20 | ENV['RUBY_GLOBAL_METHOD_CACHE_SIZE'] = <%= ENV.fetch('RUBY_GLOBAL_METHOD_CACHE_SIZE') { 'not configured'} %>
     21 | 
    22 |

    23 | For more information and a great story about how Scott Francis 24 | added this variable to Ruby 25 | see his writeup 26 | on performance tuning the Shopify app. 27 |

    28 | 29 | <% if RUBY_VERSION.to_f >= 2.1 %> 30 |

    Garbage Collection Internals

    31 |

    32 | Some of Ruby's Garbage Collection parameters are determined during by the runtime platform or compile-time configuration. 33 | These values can't be changed by environment variables but are helpful for understanding how memory is being allocated and used. 34 |

    35 | 40 | <% end %> 41 | 42 |

    Garbage Collection

    43 |

    44 | Ruby's garbage collection is highly-tunable through environment variables. 45 | Determining the right configuration requires understanding how the GC is working 46 | and how much memory is being allocated by requests to the application. 47 |

    48 |

    Controls

    49 |

    50 | Normally, the Garbage Collector runs as needed to free memory. You can also programmatically control the GC as well to stop it from running or to trigger a GC on demand. 51 |

    52 |

    53 | Is GC enabled? <%= truth_label(@gc_enabled) %> 54 |
    55 | Is GC::Profiler enabled? <%= truth_label(GC::Profiler.enabled?) %> 56 |

    57 | 58 |

    Garbage Collection Statistics

    59 | 60 | <%- gcstat = GC.stat %> 61 | <%- [:count, :major_gc_count, :minor_gc_count, 62 | :heap_available_slots, :heap_live_slots, :heap_free_slots, :heap_final_slots, :heap_marked_slots, :heap_swept_slots, 63 | :heap_sorted_length, 64 | :total_allocated_pages, :total_freed_pages, :heap_allocatable_pages, :heap_allocated_pages, :heap_eden_pages, :heap_tomb_pages, 65 | :total_allocated_objects, :total_freed_objects, 66 | :old_objects, :old_objects_limit, 67 | :remembered_wb_unprotected_objects, :remembered_wb_unprotected_objects_limit, 68 | :malloc_increase_bytes, :malloc_increase_bytes_limit, 69 | :oldmalloc_increase_bytes, :oldmalloc_increase_bytes_limit].each do |key| %> 70 | 71 | 72 | 73 | 74 | <%- end %> 75 |
    <%= key %><%= gcstat[key] %>
    76 | 77 | <% if RUBY_VERSION.to_f >= 2.1 %> 78 |

    Latest GC info

    79 | <%- dgtest = GC.latest_gc_info %> 80 |
    81 | <%- dgtest.each do |key,val| %> 82 |
    <%= key %>
    83 |
    <%= val %>
    84 | <%- end %> 85 |
    86 | <% end %> 87 | 88 |

    Configuration settings

    89 |

    90 | The following environment variables are available to be configured to tune the Garbage Collector in Ruby 2.2. 91 |

    92 |

    93 | Some useful resources for understanding how these variables affect performance can be found here: 94 |

    95 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | <%- @gc_params.each do |key, desc| %> 107 | 108 | 109 | 110 | 111 | 112 | <%- end %> 113 |
    ENVValueDescription
    <%= key %><%= ENV.fetch(key.to_s) { 'not configured'} %><%= desc %>
    114 | 115 | -------------------------------------------------------------------------------- /config/rails_config_base.yml: -------------------------------------------------------------------------------- 1 | --- 2 | :asset_host: 3 | Sets the host for the assets. Useful when CDNs are used for hosting assets, 4 | or when you want to work around the concurrency constraints built-in in browsers 5 | using different domain aliases. Shorter version of config.action_controller.asset_host. 6 | :beginning_of_week: 7 | Sets the default beginning of week for the application. Accepts 8 | a valid week day symbol (e.g. :monday). 9 | :cache_classes: 10 | Controls whether or not application classes and modules should be 11 | reloaded on each request. Defaults to false in development mode, and true in test 12 | and production modes. 13 | :consider_all_requests_local: 14 | Is a flag. If true then any error will cause detailed debugging information to 15 | be dumped in the HTTP response, and the Rails::Info controller will show the 16 | application runtime context in /rails/info/properties. 17 | :colorize_logging: 18 | Specifies whether or not to use ANSI color codes when logging information. 19 | Defaults to true. 20 | :eager_load: 21 | When true, eager loads all registered config.eager_load_namespaces. This 22 | includes your application, engines, Rails frameworks and any other registered namespace. 23 | :eager_load_namespaces: 24 | Registers namespaces that are eager loaded when config.eager_load 25 | is true. All namespaces in the list must respond to the eager_load! method. 26 | :eager_load_paths: 27 | Accepts an array of paths from which Rails will eager load on boot 28 | if cache classes is enabled. Defaults to every folder in the app directory of the 29 | application. 30 | :encoding: 31 | Sets up the application-wide encoding. Defaults to UTF-8. 32 | :exceptions_app: 33 | Sets the exceptions application invoked by the ShowException middleware 34 | when an exception happens. Defaults to ActionDispatch::PublicExceptions.new(Rails.public_path). 35 | :filter_parameters: 36 | Used for filtering out the parameters that you don't want shown 37 | in the logs, such as passwords or credit card numbers. By default, Rails filters 38 | out passwords by adding Rails.application.config.filter_parameters += [:password] 39 | in config/initializers/filter_parameter_logging.rb. Parameters filter works by partial 40 | matching regular expression. 41 | :force_ssl: 42 | 'Forces all requests to be served over HTTPS by using the ActionDispatch::SSL 43 | middleware, and sets config.action_mailer.default_url_options to 44 | be { protocol: ''https'' }. 45 | This can be configured by setting config.ssl_options - see the ActionDispatch::SSL 46 | documentation for details.' 47 | 48 | :log_formatter: 49 | Defines the formatter of the Rails logger. This option defaults to an instance 50 | of ActiveSupport::Logger::SimpleFormatter for all modes except production, 51 | where it defaults to Logger::Formatter. 52 | 53 | :log_level: 54 | Defines the verbosity of the Rails logger. This option defaults to :debug for all environments. 55 | 56 | :log_tags: 57 | Accepts a list of methods that the request object responds to. This makes it 58 | easy to tag log lines with debug information like subdomain and request id - 59 | both very helpful in debugging multi-user production applications. 60 | 61 | :logger: 62 | Accepts a logger conforming to the interface of Log4r or the default Ruby 63 | Logger class. Defaults to an instance of ActiveSupport::Logger. 64 | 65 | :middleware: 66 | Allows you to configure the application's middleware. This is covered in depth 67 | in the Configuring Middleware of the Ruby Guide. 68 | 69 | :reload_classes_only_on_change: 70 | Enables or disables reloading of classes only when tracked files change. 71 | By default tracks everything on autoload paths and is set to true. 72 | If config.cache_classes is true, this option is ignored. 73 | 74 | :session_store: 75 | Is usually set up in config/initializers/session_store.rb and specifies what class to use to 76 | store the session. Possible values are :cookie_store which is the default, :mem_cache_store, and :disabled. 77 | The last one tells Rails not to deal with sessions. Custom session stores can also be specified. 78 | 79 | :time_zone: 80 | Sets the default time zone for the application and enables time zone awareness for Active Record. 81 | 82 | :file_watcher: 83 | Is the class used to detect file updates in the file system when config.reload_classes_only_on_change 84 | is true. Rails ships with ActiveSupport::FileUpdateChecker, the default, and ActiveSupport::EventedFileUpdateChecker 85 | (this one depends on the listen gem). Custom classes must conform to the ActiveSupport::FileUpdateChecker 86 | API. 87 | :enable_dependency_loading: 88 | When true, enables autoloading, even if the application is eager loaded and 89 | config.cache_classes is set as true. Defaults to false. 90 | :debug_exception_response_format: 91 | Sets the format used in responses when errors occur in development mode. 92 | Defaults to :api for API only apps and :default for normal apps. 93 | -------------------------------------------------------------------------------- /config/rails_config_v6.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | :file_watcher: 3 | Is the class used to detect file updates in the file system when config.reload_classes_only_on_change 4 | is true. Rails ships with ActiveSupport::FileUpdateChecker, the default, and ActiveSupport::EventedFileUpdateChecker 5 | (this one depends on the listen gem). Custom classes must conform to the ActiveSupport::FileUpdateChecker 6 | API. 7 | :enable_dependency_loading: 8 | When true, enables autoloading, even if the application is eager loaded and 9 | config.cache_classes is set as true. Defaults to false. 10 | :debug_exception_response_format: 11 | Sets the format used in responses when errors occur in development mode. 12 | Defaults to :api for API only apps and :default for normal apps. 13 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: false 2 | Tuttle::Engine.routes.draw do 3 | 4 | root :to => 'home#index' 5 | 6 | namespace :rails do 7 | get '', :action => :index 8 | get :controllers, :models, :database, :schema_cache, :helpers, :assets, 9 | :routes, :instrumentation, :cache, :generators, :engines 10 | end 11 | 12 | namespace :ruby do 13 | get '', :action => :index 14 | get :miscellaneous, :tuning, :extensions, :constants 15 | end 16 | 17 | namespace :gems do 18 | get '', :action => :index 19 | get :get_process_mem, :http_clients, :json, :other 20 | end 21 | 22 | namespace :i18n do 23 | get '', :action => :index 24 | get :localize, :translations 25 | end 26 | 27 | namespace :active_support do 28 | get '', :action => :index 29 | get :dependencies, :inflectors, :time_zones 30 | end 31 | 32 | if defined?(ActiveJob) 33 | namespace :active_job do 34 | get '', :action => :index 35 | end 36 | end 37 | 38 | get '/rack_mini_profiler' => 'rack_mini_profiler#index' 39 | 40 | get '/request' => 'request#index' 41 | 42 | if defined?(ActiveModelSerializers) 43 | get '/active_model_serializers' => 'active_model_serializers#index' # 0.10.x? 44 | elsif defined?(ActiveModel::Serializer) 45 | get '/active_model_serializers' => 'active_model_serializers#index' # 0.9.x? 46 | end 47 | 48 | if defined?(Devise) 49 | get '/devise' => 'devise#index' 50 | end 51 | 52 | if defined?(CanCanCan) 53 | get '/cancancan' => 'cancancan#index' 54 | get '/cancancan/rule_tester' => 'cancancan#rule_tester' 55 | end 56 | 57 | if defined?(::ExecJS) 58 | get '/execjs' => 'execjs#index' 59 | end 60 | 61 | if defined?(::Rack::Attack) 62 | get '/rack-attack' => 'rack_attack#index' 63 | end 64 | 65 | end 66 | -------------------------------------------------------------------------------- /gemfiles/rails_5.2.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rails", "~> 5.2.0" 6 | 7 | group :development, :test do 8 | gem "rails-controller-testing" 9 | gem "jquery-rails" 10 | gem "sqlite3" 11 | gem "active_model_serializers" 12 | gem "busted" 13 | gem "cancancan" 14 | gem "devise", ">= 3.5.0" 15 | gem "rack-attack", ">= 6.5.0" 16 | gem "redis", ">= 4.0" 17 | gem "execjs" 18 | gem "get_process_mem", ">= 0.2.7" 19 | gem "memory_profiler" 20 | gem "mini_racer" 21 | gem "rack-mini-profiler", ">= 2.3.2" 22 | gem "ruby-prof", ">= 1.4.3" 23 | gem "codacy-coverage", require: false 24 | gem "rubocop", require: false 25 | end 26 | 27 | gemspec path: "../" 28 | -------------------------------------------------------------------------------- /gemfiles/rails_6.0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rails", "~> 6.0.0" 6 | 7 | group :development, :test do 8 | gem "rails-controller-testing" 9 | gem "jquery-rails" 10 | gem "sqlite3" 11 | gem "active_model_serializers" 12 | gem "busted" 13 | gem "cancancan" 14 | gem "devise", ">= 3.5.0" 15 | gem "rack-attack", ">= 6.5.0" 16 | gem "redis", ">= 4.0" 17 | gem "execjs" 18 | gem "get_process_mem", ">= 0.2.7" 19 | gem "memory_profiler" 20 | gem "mini_racer" 21 | gem "rack-mini-profiler", ">= 2.3.2" 22 | gem "ruby-prof", ">= 1.4.3" 23 | gem "codacy-coverage", require: false 24 | gem "rubocop", require: false 25 | end 26 | 27 | gemspec path: "../" 28 | -------------------------------------------------------------------------------- /gemfiles/rails_6.1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rails", "~> 6.1.0" 6 | 7 | group :development, :test do 8 | gem "rails-controller-testing" 9 | gem "jquery-rails" 10 | gem "sqlite3" 11 | gem "active_model_serializers" 12 | gem "busted" 13 | gem "cancancan" 14 | gem "devise", ">= 3.5.0" 15 | gem "rack-attack", ">= 6.5.0" 16 | gem "redis", ">= 4.0" 17 | gem "execjs" 18 | gem "get_process_mem", ">= 0.2.7" 19 | gem "memory_profiler" 20 | gem "mini_racer" 21 | gem "rack-mini-profiler", ">= 2.3.2" 22 | gem "ruby-prof", ">= 1.4.3" 23 | gem "codacy-coverage", require: false 24 | gem "rubocop", require: false 25 | end 26 | 27 | gemspec path: "../" 28 | -------------------------------------------------------------------------------- /lib/tuttle.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'tuttle/version' 3 | 4 | # TODO: clean this up so that mattr_accessor is not needed 5 | require 'active_support/core_ext/module/attribute_accessors' 6 | 7 | module Tuttle 8 | mattr_accessor :automount_engine 9 | mattr_accessor :enabled 10 | mattr_accessor :enable_profiling 11 | mattr_accessor :track_notifications 12 | 13 | autoload :Instrumenter, 'tuttle/instrumenter' 14 | 15 | def self.setup 16 | yield self 17 | end 18 | end 19 | 20 | require 'tuttle/engine' if defined?(Rails) 21 | -------------------------------------------------------------------------------- /lib/tuttle/engine.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'rails/engine' 3 | 4 | module Tuttle 5 | class Engine < ::Rails::Engine 6 | isolate_namespace Tuttle 7 | 8 | attr_accessor :reload_needed, :session_start, :session_id 9 | 10 | attr_reader :logger 11 | 12 | config.tuttle = ActiveSupport::OrderedOptions.new 13 | 14 | config.before_configuration do 15 | Tuttle::Engine.session_start = Time.current 16 | Tuttle::Engine.session_id = SecureRandom.uuid 17 | end 18 | 19 | initializer 'tuttle' do |app| 20 | app.config.tuttle.each do |k, v| 21 | Tuttle.send("#{k}=", v) 22 | end 23 | 24 | # Tuttle will be enabled automatically in development if not configured explicitly 25 | Tuttle.enabled = Rails.env.development? if Tuttle.enabled.nil? 26 | 27 | next unless Tuttle.enabled 28 | 29 | @logger = ::Logger.new(Rails.root.join('log', 'tuttle.log')) 30 | Tuttle::Engine.logger.info('Tuttle engine started') 31 | 32 | Tuttle.automount_engine = true if Tuttle.automount_engine.nil? 33 | 34 | mount_engine! if Tuttle.automount_engine 35 | use_profiling_middleware! if Tuttle.enable_profiling 36 | 37 | Tuttle::Instrumenter.initialize_tuttle_instrumenter if Tuttle.track_notifications 38 | end 39 | 40 | config.to_prepare do 41 | Tuttle::Engine.reload_needed = true 42 | end 43 | 44 | private 45 | 46 | def mount_engine! 47 | Rails.application.routes.prepend do 48 | Tuttle::Engine.logger.info('Auto-mounting /tuttle routes') 49 | mount Tuttle::Engine, at: 'tuttle' 50 | end 51 | end 52 | 53 | def use_profiling_middleware! 54 | # Add memory/cpu profiler middleware at the end of the stack 55 | Tuttle::Engine.logger.info('Using Tuttle::Middleware::RequestProfiler middleware') 56 | require 'tuttle/middleware/request_profiler' 57 | Rails.application.config.middleware.use Tuttle::Middleware::RequestProfiler 58 | end 59 | 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/tuttle/instrumenter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module Tuttle 3 | class Instrumenter 4 | 5 | mattr_accessor :events, :event_counts, :cache_events 6 | self.events = [] 7 | self.event_counts = Hash.new(0) 8 | self.cache_events = [] 9 | 10 | def self.initialize_tuttle_instrumenter 11 | # For now, only instrument non-production mode 12 | unless Rails.env.production? 13 | ActiveSupport::Notifications.subscribe(/.*/) do |*args| 14 | event = ActiveSupport::Notifications::Event.new(*args) 15 | Tuttle::Instrumenter.events << event 16 | Tuttle::Instrumenter.event_counts[event.name] += 1 17 | end 18 | end 19 | 20 | # Note: For Rails < 4.2 instrumentation is not enabled by default. 21 | # Hitting the cache inspector page will enable it for that session. 22 | Tuttle::Engine.logger.info('Initializing cache_read subscriber') 23 | ActiveSupport::Notifications.subscribe('cache_read.active_support') do |*args| 24 | app_path = Rails.root.join('app').to_s 25 | cache_call_location = caller_locations.detect { |cl| cl.path.start_with?(app_path) } 26 | event = ActiveSupport::Notifications::Event.new(*args) 27 | 28 | Tuttle::Engine.logger.info("Cache Read called: #{cache_call_location.path} on line #{cache_call_location.lineno} :: #{event.payload.inspect}") 29 | 30 | event.payload.merge!(:call_location_path => cache_call_location.path, :call_location_lineno => cache_call_location.lineno) 31 | Tuttle::Instrumenter.cache_events << event 32 | end 33 | 34 | ActiveSupport::Notifications.subscribe('cache_generate.active_support') do 35 | Tuttle::Engine.logger.info('Cache Generate called') 36 | end 37 | end 38 | 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/tuttle/presenters/action_dispatch/routing/route_wrapper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module Tuttle 3 | module Presenters 4 | module ActionDispatch 5 | module Routing 6 | class RouteWrapper < ::ActionDispatch::Routing::RouteWrapper 7 | 8 | def endpoint_or_app_name 9 | if uses_dispatcher? 10 | endpoint 11 | else 12 | rack_app.is_a?(Class) ? rack_app : rack_app.class 13 | end 14 | end 15 | 16 | def controller 17 | super if uses_dispatcher? 18 | end 19 | 20 | def action 21 | super if uses_dispatcher? 22 | end 23 | 24 | def uses_dispatcher? 25 | rack_app.respond_to?(:dispatcher?) 26 | end 27 | 28 | def route_problem 29 | # TODO: this does not handle ImplicitRender actions where the method does not exist but the template does 30 | return @route_problem if defined?(@route_problem) 31 | @route_problem = 32 | if controller_klass 33 | if requirements[:action] && controller_klass.action_methods.exclude?(action) 34 | 'Action does not exist' 35 | end 36 | elsif requirements[:controller] 37 | 'Controller does not exist' 38 | end 39 | end 40 | 41 | private 42 | 43 | def controller_klass 44 | return @controller_klass if defined?(@controller_klass) 45 | @controller_klass = 46 | if requirements[:controller].present? 47 | begin 48 | controller_reference(controller) 49 | rescue NameError 50 | # No class is defined for the give route 51 | # puts "NameError for #{requirements[:controller]}" 52 | nil 53 | end 54 | end 55 | end 56 | 57 | # Copied from /lib/action_dispatch/routing/route_set.rb 58 | def controller_reference(controller_param) 59 | const_name = "#{controller_param.camelize}Controller" 60 | ::ActiveSupport::Dependencies.constantize(const_name) 61 | end 62 | 63 | end 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/tuttle/presenters/active_record/reflection_presenter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_dependency 'tuttle/presenters/base_presenter' 3 | 4 | module Tuttle 5 | module Presenters 6 | module ActiveRecord 7 | class ReflectionPresenter < ::Tuttle::Presenters::BasePresenter 8 | 9 | def name; super.inspect end 10 | def macro; super.inspect end 11 | def type; super rescue 'Unknown' end 12 | 13 | def inverse_of 14 | if has_inverse? 15 | h.content_tag(:span, has_inverse?.inspect, class: options[:inverse_of].present? ? 'specified' : 'autodetected') 16 | end 17 | end 18 | 19 | def scoped? 20 | # TODO: potentially show the scope 21 | h.true_label(scope.present?, 'scoped') 22 | end 23 | 24 | def polymorphic? 25 | h.true_label(super, 'polymorphic') 26 | end 27 | 28 | def validate? 29 | h.true_label(super, 'validate') 30 | end 31 | 32 | def options_dependent; options[:dependent] rescue 'Unknown' end 33 | def options_class_name; options[:class_name] rescue 'Unknown' end 34 | 35 | def options_autosave 36 | h.true_label(options[:autosave].present?, 'autosave') 37 | end 38 | 39 | def options_required 40 | ## Todo handle auto-required? 41 | h.true_label(options[:required].present?, 'required') 42 | end 43 | 44 | def foreign_key; super rescue 'Unknown' end 45 | 46 | def options_other 47 | other_options = options.except(:polymorphic, :dependent, :class_name, :autosave, :before_add, :before_remove) 48 | other_options.inspect unless other_options.empty? 49 | end 50 | 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/tuttle/presenters/active_support/callbacks.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'delegate' 3 | 4 | module Tuttle 5 | module Presenters 6 | module ActiveSupport 7 | module Callbacks 8 | class CallbackWrapper < DelegateClass(::ActiveSupport::Callbacks::Callback) 9 | def safe_source_location(controller_instance) 10 | controller_instance.method(filter).try(:source_location) 11 | rescue StandardError 12 | [nil, nil] 13 | end 14 | end 15 | 16 | class CallbackChainWrapper < DelegateClass(::ActiveSupport::Callbacks::CallbackChain) 17 | delegate :size, to: :chain 18 | 19 | def each(&block) 20 | chain.map { |cb| CallbackWrapper.new(::Tuttle::Presenters::ActiveSupport::Callbacks::CallbackWrapper.new(cb)) } .each(&block) 21 | end 22 | end 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/tuttle/presenters/base_presenter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'tuttle' 3 | require 'delegate' 4 | 5 | module Tuttle 6 | module Presenters 7 | class BasePresenter < SimpleDelegator 8 | def initialize(delegate, view) 9 | @view = view 10 | super(delegate) 11 | end 12 | 13 | # noinspection RubyInstanceMethodNamingConvention 14 | def h 15 | @view 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/tuttle/presenters/rack_mini_profiler/client_settings.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module Tuttle 3 | module Presenters 4 | module RackMiniProfiler 5 | class ClientSettings < SimpleDelegator 6 | 7 | def initialize(env) 8 | rmp_cs_args = [env] 9 | rmp_cs_args += [::Rack::MiniProfiler.config.storage_instance, Time.current] if version_10? 10 | super(::Rack::MiniProfiler::ClientSettings.new(*rmp_cs_args)) 11 | end 12 | 13 | def version_10? 14 | Rack::MiniProfiler::ClientSettings.instance_method(:initialize).arity > 1 15 | end 16 | 17 | def tuttle_check_cookie 18 | version_10? ? has_valid_cookie? : has_cookie? 19 | end 20 | 21 | def tuttle_check_cookie_method 22 | version_10? ? 'has_valid_cookie?' : 'has_cookie?' 23 | end 24 | 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/tuttle/ruby_prof/fast_call_stack_printer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'ruby-prof/printers/call_stack_printer' 3 | 4 | # This is a modified version of the RubyProf::CallStackPrinter 5 | # It has been sped up by removing most link generation and 6 | # expensive HTML formatting (like coloring) 7 | 8 | module Tuttle 9 | module RubyProf 10 | # prints a HTML visualization of the call tree 11 | class FastCallStackPrinter < ::RubyProf::CallStackPrinter 12 | 13 | # Specify print options. 14 | # 15 | # options - See CallStackPrinter for based options 16 | # 17 | def print(output = STDOUT, options = {}) 18 | @output = output 19 | setup_options(options) 20 | 21 | print_header 22 | 23 | @overall_threads_time = @result.threads.inject(0) do |val, thread| 24 | val + thread.total_time 25 | end 26 | 27 | @method_full_name_cache = {}.compare_by_identity 28 | 29 | @result.threads.each do |thread| 30 | @overall_time = thread.total_time 31 | thread_info = "Thread: #{thread.id}".dup 32 | thread_info << ", Fiber: #{thread.fiber_id}" unless thread.id == thread.fiber_id 33 | thread_info << format(' (%4.2f%% ~ %f)', (@overall_time / @overall_threads_time) * 100, @overall_time) 34 | 35 | @output.print "
    #{thread_info}
    " 36 | @output.print '
      ' 37 | thread.methods.each do |m| 38 | # $stderr.print m.dump 39 | next unless m.root? 40 | m.call_infos.each do |ci| 41 | next unless ci.root? 42 | print_stack ci, @overall_time 43 | end 44 | end 45 | @output.print '
    ' 46 | end 47 | 48 | @method_full_name_cache = nil 49 | 50 | print_footer 51 | end 52 | 53 | def print_stack(call_info, parent_time) 54 | total_time = call_info.total_time 55 | percent_total = (total_time / @overall_time) * 100 56 | return unless percent_total > min_percent 57 | 58 | percent_parent = (total_time / parent_time) * 100 59 | if percent_total < threshold 60 | @output.write '
  • '.freeze 61 | else 62 | @output.write '
  • '.freeze 63 | end 64 | 65 | expanded = percent_total >= expansion 66 | kids = call_info.children 67 | 68 | toggle_href = if kids.empty? || kids.none? {|ci| (ci.total_time / @overall_time) * 100 >= threshold} 69 | ''.freeze 70 | elsif expanded 71 | ''.freeze 72 | else 73 | ''.freeze 74 | end 75 | @output.write toggle_href 76 | 77 | method = call_info.target 78 | @output.printf "%4.2f%% (%4.2f%%) %s [%d calls, %d total]\n".freeze, 79 | percent_total, percent_parent, 80 | method_full_name(method), call_info.called, method.called 81 | unless kids.empty? 82 | if expanded 83 | @output.write '
      '.freeze 84 | else 85 | @output.write '
        '.freeze 86 | end 87 | kids.sort_by!(&:total_time).reverse_each do |callinfo| 88 | print_stack callinfo, total_time 89 | end 90 | @output.write '
      '.freeze 91 | end 92 | @output.write ''.freeze 93 | end 94 | 95 | def method_full_name(method) 96 | @method_full_name_cache[method] ||= begin 97 | # Use ruby-prof klass_name only for non-Classes or klasses that do not report a name 98 | # This prevents klass.inspect from being used which prints complex names for ActiveRecord classes 99 | klass_name = method.klass && method.klass.class == Class && method.klass.name || method.klass_name 100 | h("#{klass_name}##{method.method_name}") 101 | end 102 | end 103 | 104 | def threshold 105 | @threshold ||= super 106 | end 107 | 108 | def expansion 109 | @expansion ||= super 110 | end 111 | 112 | def application 113 | @application ||= @options.delete(:application) || $PROGRAM_NAME 114 | end 115 | 116 | def print_title_bar 117 | @output.puts <<-"end_title_bar" 118 |
      119 | Call tree for application #{h application} #{h arguments}
      120 | Generated on #{Time.current} with options #{h @options.inspect}
      121 |
      122 | end_title_bar 123 | end 124 | 125 | def print_css 126 | super 127 | @output.puts <<-CSS_OVERRIDE 128 | 133 | CSS_OVERRIDE 134 | end 135 | 136 | end 137 | end 138 | end 139 | -------------------------------------------------------------------------------- /lib/tuttle/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tuttle 4 | VERSION = '0.1.0' 5 | end 6 | -------------------------------------------------------------------------------- /test/controllers/tuttle/active_job_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Tuttle 4 | class ActiveJobControllerTest < ActionController::TestCase 5 | 6 | test 'should get index' do 7 | skip unless defined?(ActiveJob) 8 | get :index 9 | assert_response :success 10 | end 11 | 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/controllers/tuttle/active_model_serializers_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Tuttle 4 | class ActiveModelSerializersControllerTest < ActionController::TestCase 5 | 6 | test 'should get index' do 7 | skip unless defined?(::ActiveModel::Serializer) 8 | get :index 9 | assert_response :success 10 | end 11 | 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/controllers/tuttle/active_support_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Tuttle 4 | class ActiveSupportControllerTest < ActionController::TestCase 5 | 6 | test 'should get index' do 7 | get :index 8 | assert_response :success 9 | end 10 | 11 | test 'should get dependencies' do 12 | get :dependencies 13 | assert_response :success 14 | end 15 | 16 | test 'should get time_zones' do 17 | get :time_zones 18 | assert_response :success 19 | end 20 | 21 | test 'should get inflectors' do 22 | get :inflectors 23 | assert_response :success 24 | assert_not_nil assigns(:test_word) 25 | assert_not_nil assigns(:plurals) 26 | end 27 | 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/controllers/tuttle/cancancan_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Tuttle 4 | class CancancanControllerTest < ActionController::TestCase 5 | 6 | test 'should get index' do 7 | get :index 8 | assert_response :success 9 | 10 | assert_not_nil assigns(:cancan_user) 11 | assert_not_nil assigns(:ability) 12 | end 13 | 14 | test 'should get rule_tester' do 15 | get :rule_tester 16 | assert_response :success 17 | 18 | assert_not_nil assigns(:action) 19 | assert_not_nil assigns(:cancan_user) 20 | assert_not_nil assigns(:ability) 21 | assert_nil assigns(:subject) 22 | end 23 | 24 | test 'should get rule_tester with subject' do 25 | params = { :action_name => 'manage', :subject_class => 'User' } 26 | if Rails.version < '5' 27 | get :rule_tester, params 28 | else 29 | get :rule_tester, :params => params 30 | end 31 | 32 | assert_response :success 33 | 34 | assert_not_nil assigns(:action) 35 | assert_not_nil assigns(:cancan_user) 36 | assert_not_nil assigns(:ability) 37 | assert_not_nil assigns(:subject) 38 | end 39 | 40 | test 'should get rule_tester with subject and subject_id' do 41 | params = { :action_name => 'manage', :subject_class => 'User', :subject_id => 1 } 42 | if Rails.version < '5' 43 | get :rule_tester, params 44 | else 45 | get :rule_tester, :params => params 46 | end 47 | 48 | assert_response :success 49 | 50 | assert_not_nil assigns(:action) 51 | assert_not_nil assigns(:cancan_user) 52 | assert_not_nil assigns(:ability) 53 | assert_not_nil assigns(:subject) 54 | end 55 | 56 | test 'should get rule_tester with invalid subject' do 57 | params = { :action_name => 'manage', :subject_class => 'ClassDoesNotExist' } 58 | if Rails.version < '5' 59 | get :rule_tester, params 60 | else 61 | get :rule_tester, :params => params 62 | end 63 | 64 | assert_response :success 65 | 66 | assert_not_nil assigns(:action) 67 | assert_not_nil assigns(:cancan_user) 68 | assert_not_nil assigns(:ability) 69 | assert_nil assigns(:subject) 70 | end 71 | 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /test/controllers/tuttle/devise_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Tuttle 4 | class DeviseControllerTest < ActionController::TestCase 5 | 6 | test 'should get index' do 7 | get :index 8 | assert_response :success 9 | end 10 | 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/controllers/tuttle/execjs_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Tuttle 4 | class ExecjsControllerTest < ActionController::TestCase 5 | 6 | test 'should get index' do 7 | get :index 8 | assert_response :success 9 | end 10 | 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/controllers/tuttle/gems_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Tuttle 4 | class GemsControllerTest < ActionController::TestCase 5 | 6 | test 'should get index' do 7 | get :index 8 | assert_response :success 9 | end 10 | 11 | test 'should get http_clients' do 12 | get :http_clients 13 | assert_response :success 14 | end 15 | 16 | test 'should get json' do 17 | get :json 18 | assert_response :success 19 | end 20 | 21 | test 'should get get_process_mem' do 22 | get :get_process_mem 23 | assert_response :success 24 | end 25 | 26 | test 'should get other' do 27 | get :other 28 | assert_response :success 29 | end 30 | 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/controllers/tuttle/home_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Tuttle 4 | class HomeControllerTest < ActionController::TestCase 5 | 6 | test 'should get index' do 7 | get :index 8 | assert_response :success 9 | assert_select 'h1', 'Tuttle Dashboard' 10 | end 11 | 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/controllers/tuttle/i18n_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Tuttle 4 | class I18nControllerTest < ActionController::TestCase 5 | 6 | test 'should get index' do 7 | get :index 8 | assert_response :success 9 | 10 | assert_select 'h1', 'Internationalization' 11 | end 12 | 13 | test 'should get localize' do 14 | get :localize 15 | assert_response :success 16 | 17 | assert_select 'h1', 'Localization' 18 | end 19 | 20 | test 'should get translations' do 21 | get :translations 22 | assert_response :success 23 | 24 | assert_select 'h1', 'Translations for :en' 25 | end 26 | 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/controllers/tuttle/rack_attack_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Tuttle 4 | class RackAttackControllerTest < ActionController::TestCase 5 | 6 | test 'should get index' do 7 | get :index 8 | assert_response :success 9 | end 10 | 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/controllers/tuttle/rack_mini_profiler_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Tuttle 4 | class RackMiniProfilerControllerTest < ActionController::TestCase 5 | 6 | test 'should get index' do 7 | get :index 8 | assert_response :success 9 | end 10 | 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/controllers/tuttle/rails_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Tuttle 4 | class RailsControllerTest < ActionController::TestCase 5 | 6 | test 'should get index' do 7 | get :index 8 | assert_response :success 9 | 10 | assert_select "#config tr[data-config-key=enable_dependency_loading]", 1 11 | end 12 | 13 | test 'should get controllers' do 14 | get :controllers 15 | assert_response :success 16 | assert_not_nil assigns(:controllers) 17 | end 18 | 19 | test 'should get models' do 20 | get :models 21 | assert_response :success 22 | assert_not_nil assigns(:models) 23 | end 24 | 25 | test 'should get database' do 26 | get :database 27 | assert_response :success 28 | assert_not_nil assigns(:conn) 29 | assert_not_nil assigns(:data_sources) 30 | end 31 | 32 | test 'should get schema_cache' do 33 | get :schema_cache 34 | assert_response :success 35 | assert_not_nil assigns(:schema_cache_filename) 36 | assert_not_nil assigns(:schema_cache) 37 | assert_not_nil assigns(:connection_schema_cache) 38 | end 39 | 40 | test 'should get engines' do 41 | get :engines 42 | assert_response :success 43 | assert_not_nil assigns(:engines) 44 | end 45 | 46 | test 'should get generators' do 47 | get :generators 48 | assert_response :success 49 | assert_not_nil assigns(:generators) 50 | end 51 | 52 | test 'should get helpers' do 53 | get :helpers 54 | assert_response :success 55 | assert_not_nil assigns(:helpers) 56 | end 57 | 58 | test 'should get assets' do 59 | get :assets 60 | assert_response :success 61 | assert_not_nil assigns(:sprockets_env) 62 | assert_not_nil assigns(:assets_config) 63 | end 64 | 65 | test 'should get routes' do 66 | get :routes 67 | assert_response :success 68 | assert_not_nil assigns(:routes) 69 | end 70 | 71 | test 'should get instrumentation' do 72 | skip "This is hanging sometimes" 73 | get :instrumentation 74 | assert_response :success 75 | assert_not_nil assigns(:events) 76 | assert_not_nil assigns(:event_counts) 77 | end 78 | 79 | test 'should get cache' do 80 | get :cache 81 | assert_response :success 82 | assert_not_nil assigns(:cache) 83 | end 84 | 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /test/controllers/tuttle/request_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Tuttle 4 | class RequestControllerTest < ActionController::TestCase 5 | 6 | test 'should get index' do 7 | get :index 8 | assert_response :success 9 | end 10 | 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/controllers/tuttle/ruby_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Tuttle 4 | class RubyControllerTest < ActionController::TestCase 5 | 6 | test 'should get index' do 7 | get :index 8 | assert_response :success 9 | end 10 | 11 | test 'should get tuning' do 12 | get :tuning 13 | assert_response :success 14 | end 15 | 16 | test 'should get miscellaneous' do 17 | get :miscellaneous 18 | assert_response :success 19 | end 20 | 21 | test 'should get extensions' do 22 | get :extensions 23 | assert_response :success 24 | end 25 | 26 | test 'should get constants' do 27 | get :constants 28 | assert_response :success 29 | end 30 | 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # Add your own tasks in files placed in lib/tasks ending in .rake, 3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 4 | 5 | require File.expand_path('../config/application', __FILE__) 6 | 7 | Dummy::Application.load_tasks 8 | -------------------------------------------------------------------------------- /test/dummy/app/assets/config/manifest.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgynn/tuttle/6e150bf07b13f8833b55ac7028e805397fb60f87/test/dummy/app/assets/config/manifest.js -------------------------------------------------------------------------------- /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 | // the 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 jquery 14 | //= require jquery_ujs 15 | //= require_tree . 16 | -------------------------------------------------------------------------------- /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_self 12 | *= require_tree . 13 | */ 14 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | end 4 | -------------------------------------------------------------------------------- /test/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /test/dummy/app/models/ability.rb: -------------------------------------------------------------------------------- 1 | class Ability 2 | include CanCan::Ability 3 | 4 | def initialize(_user) 5 | can :manage, :all 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /test/dummy/app/models/note.rb: -------------------------------------------------------------------------------- 1 | class Note < ApplicationRecord 2 | belongs_to :user 3 | end 4 | -------------------------------------------------------------------------------- /test/dummy/app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | # Include default devise modules. Others available are: 3 | # :confirmable, :lockable, :timeoutable and :omniauthable 4 | devise :database_authenticatable, :registerable, 5 | :recoverable, :rememberable, :trackable, :validatable 6 | has_many :notes 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dummy 5 | <%= stylesheet_link_tag "application", :media => "all" %> 6 | <%= javascript_include_tag "application" %> 7 | <%= csrf_meta_tags %> 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /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 Dummy::Application 5 | -------------------------------------------------------------------------------- /test/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | Bundler.require(*Rails.groups) 6 | require 'tuttle' 7 | 8 | module Dummy 9 | class Application < Rails::Application 10 | # Settings in config/environments/* take precedence over those specified here. 11 | # Application configuration should go into files in config/initializers 12 | # -- all .rb files in that directory are automatically loaded. 13 | 14 | # Custom directories with classes and modules you want to be autoloadable. 15 | # config.autoload_paths += %W(#{config.root}/extras) 16 | 17 | # Only load the plugins named here, in the order given (default is alphabetical). 18 | # :all can be used as a placeholder for all plugins not explicitly named. 19 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 20 | 21 | # Activate observers that should always be running. 22 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer 23 | 24 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 25 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 26 | # config.time_zone = 'Central Time (US & Canada)' 27 | 28 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 29 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 30 | # config.i18n.default_locale = :de 31 | 32 | # Configure the default encoding used in templates for Ruby 1.9. 33 | config.encoding = 'utf-8' 34 | 35 | # Configure sensitive parameters which will be filtered from the log file. 36 | config.filter_parameters += [:password] 37 | 38 | # Enable escaping HTML in JSON. 39 | config.active_support.escape_html_entities_in_json = true 40 | 41 | # Use SQL instead of Active Record's schema dumper when creating the database. 42 | # This is necessary if your schema can't be completely dumped by the schema dumper, 43 | # like if you have constraints or database-specific column types 44 | # config.active_record.schema_format = :sql 45 | 46 | # Enable the asset pipeline 47 | config.assets.enabled = true 48 | 49 | # Version of your assets, change this if you want to expire all your assets 50 | config.assets.version = '1.0' 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /test/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | gemfile = File.expand_path('../../../../Gemfile', __FILE__) 3 | 4 | if File.exist?(gemfile) 5 | ENV['BUNDLE_GEMFILE'] ||= gemfile 6 | end 7 | 8 | require 'bundler' 9 | Bundler.setup 10 | 11 | $LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__) 12 | -------------------------------------------------------------------------------- /test/dummy/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | development: 7 | adapter: sqlite3 8 | database: db/development.sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | # Warning: The database defined as "test" will be erased and 13 | # re-generated from your development database when you run "rake". 14 | # Do not set this db to the same as development or production. 15 | test: 16 | adapter: sqlite3 17 | database: db/test.sqlite3 18 | pool: 5 19 | timeout: 5000 20 | 21 | production: 22 | adapter: sqlite3 23 | database: db/production.sqlite3 24 | pool: 5 25 | timeout: 5000 26 | -------------------------------------------------------------------------------- /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/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 | # Log error messages when you accidentally call methods on nil. 10 | config.whiny_nils = true 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 | # Print deprecation notices to the Rails logger 20 | config.active_support.deprecation = :log 21 | 22 | # Only use best-standards-support built into browsers 23 | config.action_dispatch.best_standards_support = :builtin 24 | 25 | # Do not compress assets 26 | config.assets.compress = false 27 | 28 | # Expands the lines which load the assets 29 | config.assets.debug = true 30 | end 31 | -------------------------------------------------------------------------------- /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 | # Configure static asset server for tests with Cache-Control for performance 11 | # config.serve_static_assets = true 12 | config.public_file_server.enabled = true 13 | config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=3600' } 14 | 15 | if Rails::VERSION::STRING >= '5.2' && Rails::VERSION::STRING < '6' 16 | config.active_record.sqlite3.represent_boolean_as_integer = true 17 | end 18 | 19 | config.eager_load = false 20 | config.active_support.test_order = :sorted 21 | 22 | # Show full error reports and disable caching 23 | config.consider_all_requests_local = true 24 | config.action_controller.perform_caching = false 25 | 26 | # Raise exceptions instead of rendering exception templates 27 | config.action_dispatch.show_exceptions = false 28 | 29 | # Disable request forgery protection in test environment 30 | config.action_controller.allow_forgery_protection = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | 37 | # Print deprecation notices to the stderr 38 | config.active_support.deprecation = :stderr 39 | end 40 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 4 | # (all these examples are active by default): 5 | # ActiveSupport::Inflector.inflections do |inflect| 6 | # inflect.plural /^(ox)$/i, '\1en' 7 | # inflect.singular /^(ox)en/i, '\1' 8 | # inflect.irregular 'person', 'people' 9 | # inflect.uncountable %w( fish sheep ) 10 | # end 11 | # 12 | # These inflection rules are supported but not enabled by default: 13 | # ActiveSupport::Inflector.inflections do |inflect| 14 | # inflect.acronym 'RESTful' 15 | # end 16 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rails generate session_migration") 8 | # Dummy::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/tuttle.rb: -------------------------------------------------------------------------------- 1 | # Test with setting an application-level config 2 | Rails.application.config.tuttle.enabled = true 3 | 4 | # Test with setting properties directly 5 | Tuttle.setup do |config| 6 | config.automount_engine = true 7 | config.track_notifications = true 8 | end 9 | 10 | require 'tuttle/middleware/request_profiler' 11 | Rails.application.config.middleware.use Tuttle::Middleware::RequestProfiler 12 | -------------------------------------------------------------------------------- /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] 9 | end 10 | 11 | # Disable root element in JSON by default. 12 | ActiveSupport.on_load(:active_record) do 13 | self.include_root_in_json = false 14 | end 15 | -------------------------------------------------------------------------------- /test/dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | hello: "Hello world" 6 | -------------------------------------------------------------------------------- /test/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | 3 | devise_for :users 4 | 5 | end 6 | -------------------------------------------------------------------------------- /test/dummy/config/secrets.yml: -------------------------------------------------------------------------------- 1 | development: 2 | secret_key_base: <%= ENV['RAILS_SECRET_TOKEN'] %> 3 | 4 | test: 5 | secret_key_base: <%= ENV.fetch("RAILS_SECRET_TOKEN") { 'test' * 10 } %> 6 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20141229204528_devise_create_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseCreateUsers < ActiveRecord::Migration 2 | def change 3 | create_table(:users) do |t| 4 | ## Database authenticatable 5 | t.string :email, null: false, default: "" 6 | t.string :encrypted_password, null: false, default: "" 7 | 8 | ## Recoverable 9 | t.string :reset_password_token 10 | t.datetime :reset_password_sent_at 11 | 12 | ## Rememberable 13 | t.datetime :remember_created_at 14 | 15 | ## Trackable 16 | t.integer :sign_in_count, default: 0, null: false 17 | t.datetime :current_sign_in_at 18 | t.datetime :last_sign_in_at 19 | t.string :current_sign_in_ip 20 | t.string :last_sign_in_ip 21 | 22 | ## Confirmable 23 | # t.string :confirmation_token 24 | # t.datetime :confirmed_at 25 | # t.datetime :confirmation_sent_at 26 | # t.string :unconfirmed_email # Only if using reconfirmable 27 | 28 | ## Lockable 29 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts 30 | # t.string :unlock_token # Only if unlock strategy is :email or :both 31 | # t.datetime :locked_at 32 | 33 | 34 | t.timestamps 35 | end 36 | 37 | add_index :users, :email, unique: true 38 | add_index :users, :reset_password_token, unique: true 39 | # add_index :users, :confirmation_token, unique: true 40 | # add_index :users, :unlock_token, unique: true 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20171223190950_create_notes.rb: -------------------------------------------------------------------------------- 1 | class CreateNotes < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :notes do |t| 4 | t.belongs_to :user 5 | t.timestamps 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/dummy/db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 20171223190950) do 14 | 15 | create_table "notes", force: :cascade do |t| 16 | t.integer "user_id" 17 | t.datetime "created_at", null: false 18 | t.datetime "updated_at", null: false 19 | t.index ["user_id"], name: "index_notes_on_user_id" 20 | end 21 | 22 | create_table "users", force: :cascade do |t| 23 | t.string "email", default: "", null: false 24 | t.string "encrypted_password", default: "", null: false 25 | t.string "reset_password_token" 26 | t.datetime "reset_password_sent_at" 27 | t.datetime "remember_created_at" 28 | t.integer "sign_in_count", default: 0, null: false 29 | t.datetime "current_sign_in_at" 30 | t.datetime "last_sign_in_at" 31 | t.string "current_sign_in_ip" 32 | t.string "last_sign_in_ip" 33 | t.datetime "created_at" 34 | t.datetime "updated_at" 35 | t.index ["email"], name: "index_users_on_email", unique: true 36 | t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true 37 | end 38 | 39 | end 40 | -------------------------------------------------------------------------------- /test/dummy/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
      22 |

      The page you were looking for doesn't exist.

      23 |

      You may have mistyped the address or the page may have moved.

      24 |
      25 | 26 | 27 | -------------------------------------------------------------------------------- /test/dummy/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
      22 |

      The change you wanted was rejected.

      23 |

      Maybe you tried to change something you didn't have access to.

      24 |
      25 | 26 | 27 | -------------------------------------------------------------------------------- /test/dummy/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
      22 |

      We're sorry, but something went wrong.

      23 |
      24 | 25 | 26 | -------------------------------------------------------------------------------- /test/dummy/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgynn/tuttle/6e150bf07b13f8833b55ac7028e805397fb60f87/test/dummy/public/favicon.ico -------------------------------------------------------------------------------- /test/dummy/script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /test/dummy/test/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | one: 2 | id: 1 3 | email: "user1@test.com" 4 | two: 5 | id: 2 6 | email: "user2@test.com" 7 | -------------------------------------------------------------------------------- /test/dummy/test/models/user_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/helpers/application_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Tuttle 4 | class ActionHelperTest < ActiveSupport::TestCase 5 | 6 | include Tuttle::ApplicationHelper 7 | 8 | test 'tuttle_redacted should redact passwords' do 9 | test_hash = { 'password' => 'PASSWORD', 'key1' => 'VALUE1' } 10 | test_results = Hash[tuttle_redacted(test_hash.each) { |key, value| [key, value] }] 11 | assert_equal '--HIDDEN--', test_results['password'] 12 | assert_equal test_hash['key1'], test_results['key1'] 13 | end 14 | 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/integration/profiling_middleware_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ProfilingMiddlewareTest < ActionDispatch::IntegrationTest 4 | 5 | # This temporarily sets verbosity to nil to deal with overly-strict HTML parsing 6 | def assert_select_quietly(*args) 7 | verbosity = $-v 8 | $-v = nil 9 | assert_select(*args) 10 | $-v = verbosity 11 | end 12 | 13 | test 'no middleware' do 14 | get '/tuttle' 15 | assert_select 'h1', 'Tuttle Dashboard' 16 | assert_response :success 17 | end 18 | 19 | test 'memory profiling middleware' do 20 | get '/tuttle?tuttle-profiler=memory_profiler' 21 | assert response.body.start_with?('Report from Tuttle::Middeware::RequestProfiler') 22 | assert_response :success 23 | end 24 | 25 | test 'cpu profiling middleware' do 26 | get '/tuttle?tuttle-profiler=ruby-prof' 27 | assert_select_quietly 'title', 'ruby-prof call tree' 28 | assert_response :success 29 | end 30 | 31 | test 'cpu profiling middleware with fast stack' do 32 | get '/tuttle?tuttle-profiler=ruby-prof&ruby-prof_printer=fast_stack' 33 | assert_select_quietly 'title', 'ruby-prof call tree' 34 | assert_response :success 35 | end 36 | 37 | test 'cpu profiling middleware with stack report' do 38 | get '/tuttle?tuttle-profiler=ruby-prof&ruby-prof_printer=stack' 39 | assert_select_quietly 'title', 'ruby-prof call tree' 40 | assert_response :success 41 | end 42 | 43 | test 'cpu profiling middleware with flat report' do 44 | get '/tuttle?tuttle-profiler=ruby-prof&ruby-prof_printer=flat' 45 | assert response.body.start_with?('Measure Mode: wall_time') 46 | assert_response :success 47 | end 48 | 49 | test 'cpu profiling middleware with graph report' do 50 | get '/tuttle?tuttle-profiler=ruby-prof&ruby-prof_printer=graph' 51 | assert_select_quietly 'h1', 'Wall_time' 52 | assert_response :success 53 | end 54 | 55 | test 'busted profiling middleware' do 56 | skip # Need to document how to enable dtrace 57 | skip unless defined?(::Busted) && Busted::Tracer.exists? 58 | get '/tuttle?tuttle-profiler=busted' 59 | assert response.body.include? 'Caches Request Observer' 60 | assert_response :success 61 | end 62 | 63 | end 64 | -------------------------------------------------------------------------------- /test/presenters/active_record_reflection_presenter_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'tuttle/presenters/active_record/reflection_presenter' 3 | 4 | module Tuttle 5 | class ActiveRecordReflectionPresenter < ActionView::TestCase 6 | helper ::Tuttle::Engine.helpers 7 | 8 | def test_user_has_many_notes 9 | # Setup 10 | user_notes_association = User.reflect_on_association(:notes) 11 | presenter = ::Tuttle::Presenters::ActiveRecord::ReflectionPresenter.new(user_notes_association, view) 12 | 13 | # Delegated methods 14 | assert_equal :notes.inspect, presenter.name 15 | assert_equal :has_many.inspect, presenter.macro 16 | assert_nil presenter.type 17 | assert_equal "user_id", presenter.foreign_key 18 | 19 | # Presenter Methods 20 | assert_equal ":user", presenter.inverse_of 21 | assert_nil presenter.scoped? 22 | assert_nil presenter.options_dependent 23 | assert_nil presenter.options_class_name 24 | assert_nil presenter.options_autosave 25 | assert_nil presenter.options_required 26 | assert_nil presenter.options_other 27 | end 28 | 29 | def test_note_belongs_to_user 30 | # Setup 31 | note_user_association = Note.reflect_on_association(:user) 32 | presenter = ::Tuttle::Presenters::ActiveRecord::ReflectionPresenter.new(note_user_association, view) 33 | 34 | # Delegated methods 35 | assert_equal :user.inspect, presenter.name 36 | assert_equal :belongs_to.inspect, presenter.macro 37 | assert_nil presenter.type 38 | assert_equal "user_id", presenter.foreign_key 39 | 40 | # Presenter Methods 41 | assert_nil presenter.inverse_of 42 | assert_nil presenter.scoped? 43 | assert_nil presenter.options_dependent 44 | assert_nil presenter.options_class_name 45 | assert_nil presenter.options_autosave 46 | assert_nil presenter.options_required 47 | assert_nil presenter.options_other 48 | end 49 | 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # Configure Rails Environment 2 | ENV['RAILS_ENV'] = 'test' 3 | 4 | require 'simplecov' 5 | SimpleCov.start 'rails' do 6 | add_group 'Presenters', 'lib/tuttle/presenters' 7 | end 8 | 9 | if ENV['CODACY_PROJECT_TOKEN'] 10 | require 'codacy-coverage' 11 | Codacy::Reporter.start 12 | end 13 | 14 | require File.expand_path('../dummy/config/environment.rb', __FILE__) 15 | require 'rails/test_help' 16 | 17 | Rails.backtrace_cleaner.remove_silencers! 18 | 19 | # Load support files - current there are no support files needed 20 | # Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } 21 | 22 | # Load fixtures from the engine 23 | if ActiveSupport::TestCase.method_defined?(:fixture_path=) 24 | ActiveSupport::TestCase.fixture_path = File.expand_path('../fixtures', __FILE__) 25 | end 26 | 27 | # rubocop:disable Style/ClassAndModuleChildren 28 | class ActiveSupport::TestCase 29 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 30 | fixtures :all 31 | 32 | # Add more helper methods to be used by all tests here... 33 | end 34 | 35 | require 'devise/version' 36 | # rubocop:disable Style/MixinUsage 37 | class ActionController::TestCase 38 | if Devise::VERSION < '4.2' 39 | include Devise::TestHelpers 40 | else 41 | include Devise::Test::ControllerHelpers 42 | end 43 | 44 | setup do 45 | @routes = Tuttle::Engine.routes 46 | end 47 | end 48 | # rubocop:enable Style/MixinUsage 49 | # rubocop:enable Style/ClassAndModuleChildren 50 | -------------------------------------------------------------------------------- /test/tuttle_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class TuttleTest < ActiveSupport::TestCase 4 | test 'truth' do 5 | assert_kind_of Module, Tuttle 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /tuttle.gemspec: -------------------------------------------------------------------------------- 1 | lib = File.expand_path('../lib', __FILE__) 2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 3 | require 'tuttle/version' 4 | 5 | Gem::Specification.new do |s| 6 | s.name = 'tuttle' 7 | s.version = Tuttle::VERSION 8 | s.authors = ['Dave Gynn'] 9 | s.email = ['davegynn@gmail.com'] 10 | s.homepage = 'https://github.com/dgynn/tuttle' 11 | s.summary = 'Rails runtime configuration inspector' 12 | s.description = 'Tuttle is a tool for Rails developers to inspect the runtime configuration information of their applications, libraries, and frameworks.' 13 | 14 | s.license = 'MIT' 15 | 16 | s.files = Dir['{app,config,lib}/**/*'] + %w[MIT-LICENSE Rakefile README.md CHANGELOG.md] 17 | 18 | s.required_ruby_version = '>= 2.7.0' 19 | 20 | s.add_dependency 'rails', '>= 5.2.0' 21 | 22 | s.add_development_dependency 'appraisal', '>= 2.4.0' 23 | end 24 | --------------------------------------------------------------------------------