├── .rspec ├── spec ├── fixtures │ └── source.rb ├── lecter_spec.rb ├── spec_helper.rb ├── formatter_headers_spec.rb ├── formatter_payload_spec.rb ├── html_generator_spec.rb ├── html_row_spec.rb └── requester_spec.rb ├── Gemfile ├── lib ├── lecter │ ├── version.rb │ ├── engine.rb │ ├── railtie.rb │ ├── formatter_headers.rb │ ├── formatter_payload.rb │ ├── rack.rb │ ├── trace_point.rb │ ├── html_row.rb │ ├── requester.rb │ └── html_generator.rb └── lecter.rb ├── bin ├── setup ├── console └── rails ├── Rakefile ├── config ├── routes.rb └── locales │ ├── en.yml │ └── ru.yml ├── .gitignore ├── CHANGELOG.md ├── .rubocop.yml ├── .all-contributorsrc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── PULL_REQUEST_TEMPLATE.md ├── LICENSE.txt ├── app ├── controllers │ └── lecter │ │ └── diagnosis_controller.rb └── views │ ├── lecter │ └── diagnosis │ │ ├── new.html.erb │ │ └── show.html.erb │ └── layouts │ └── lecter.html.erb ├── CONTRIBUTING.md ├── lecter.gemspec ├── Gemfile.lock ├── .circleci └── config.yml ├── CODE_OF_CONDUCT.md └── README.md /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | --require spec_helper 4 | -------------------------------------------------------------------------------- /spec/fixtures/source.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | some ruby code 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gemspec 6 | -------------------------------------------------------------------------------- /lib/lecter/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Lecter 4 | VERSION = '0.2.0' 5 | end 6 | -------------------------------------------------------------------------------- /lib/lecter/engine.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Lecter 4 | class Engine < ::Rails::Engine 5 | isolate_namespace Lecter 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler/gem_tasks' 4 | require 'rspec/core/rake_task' 5 | 6 | RSpec::Core::RakeTask.new(:spec) 7 | 8 | task default: :spec 9 | -------------------------------------------------------------------------------- /spec/lecter_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe Lecter do 4 | it 'has a version number' do 5 | expect(Lecter::VERSION).not_to be nil 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Lecter::Engine.routes.draw do 4 | root 'diagnosis#new' 5 | 6 | resource :diagnosis, only: %w[new create show], controller: :diagnosis 7 | end 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | 10 | # rspec failure tracking 11 | .rspec_status 12 | .idea/ 13 | *.gem 14 | coverage 15 | -------------------------------------------------------------------------------- /lib/lecter/railtie.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Lecter 4 | class LecterRailtie < Rails::Railtie 5 | initializer 'lecter.configure_rails_initialization' do |app| 6 | app.middleware.use Lecter::Rack 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'bundler/setup' 5 | require 'lecter' 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. 9 | 10 | # (If you use this, don't forget to add pry to your Gemfile!) 11 | # require "pry" 12 | # Pry.start 13 | 14 | require 'irb' 15 | IRB.start(__FILE__) 16 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | layouts: 3 | lecter: 4 | new_diagnosis: 'New diagnosis' 5 | lecter: 6 | diagnosis: 7 | show: 8 | title: 'Diagnosis results' 9 | executed_code: 'Executed code:' 10 | new: 11 | title: 'New diagnosis' 12 | endpoint: 'Endpoint' 13 | body_raw: 'Body (raw)' 14 | method: 'HTTP method' 15 | send: 'Send request' 16 | headers: 'Headers' 17 | -------------------------------------------------------------------------------- /config/locales/ru.yml: -------------------------------------------------------------------------------- 1 | ru: 2 | layouts: 3 | lecter: 4 | new_diagnosis: 'Новый диагноз' 5 | lecter: 6 | diagnosis: 7 | show: 8 | title: 'Диагноз' 9 | executed_code: 'Используемый код:' 10 | new: 11 | title: 'Новый диагноз' 12 | endpoint: 'Абсолютный путь' 13 | body_raw: 'Тело запроса' 14 | method: 'HTTP метод' 15 | send: 'Послать запрос' 16 | headers: 'Заголовки' 17 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler/setup' 4 | require 'lecter' 5 | require 'simplecov' 6 | 7 | SimpleCov.start 8 | 9 | RSpec.configure do |config| 10 | # Enable flags like --only-failures and --next-failure 11 | config.example_status_persistence_file_path = '.rspec_status' 12 | 13 | # Disable RSpec exposing methods globally on `Module` and `main` 14 | config.disable_monkey_patching! 15 | 16 | config.expect_with :rspec do |c| 17 | c.syntax = :expect 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/lecter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'lecter/engine' if defined?(Rails::Engine) 4 | require 'lecter/formatter_payload' 5 | require 'lecter/html_generator' 6 | require 'lecter/html_row' 7 | require 'lecter/rack' 8 | require 'lecter/railtie' if defined?(Rails::Railtie) 9 | require 'lecter/requester' 10 | require 'lecter/version' 11 | require 'lecter/trace_point' 12 | require 'lecter/formatter_headers' 13 | 14 | require 'rest-client' 15 | 16 | module Lecter 17 | AVAILABLE_METHODS = %w[GET POST PUT PATCH DELETE].freeze 18 | DEFAULT_METHOD = 'GET' 19 | end 20 | -------------------------------------------------------------------------------- /spec/formatter_headers_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | RSpec.describe Lecter::FormatterHeaders, type: :service do 6 | let(:headers) { 'content_type=text/html,accept=*/*' } 7 | 8 | subject { described_class.new(headers).call } 9 | 10 | it 'returns a hash' do 11 | is_expected.to eq({ 'content_type' => 'text/html', 'accept' => '*/*' }) 12 | end 13 | 14 | context 'if headers is empty string' do 15 | let(:headers) { '' } 16 | 17 | it 'returns an empty hash' do 18 | is_expected.to eq({}) 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change log 2 | 3 | ## master (unreleased) 4 | 5 | ### New features 6 | ### Bug fixes 7 | ### Changes 8 | 9 | ## 0.2.0 (2022-02-15) 10 | 11 | ### New features 12 | 13 | * [issue#74](https://github.com/Neodelf/lecter/pull/79): Add the headers parameters in the form ([@neodelf][]) 14 | 15 | ### Changes 16 | 17 | * [PR](https://github.com/Neodelf/lecter/pull/69): Set the simplecov gem version to 0.17.1 ([@neodelf][]) 18 | * [commit](https://github.com/Neodelf/lecter/commit/73e0ed69cf8e1773abed9e9b735e0742aa63dade): Update makeup. ([@neodelf][]) 19 | 20 | [@neodelf]: https://github.com/neodelf 21 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # This command will automatically be run when you run "rails" with Rails gems 5 | # installed from the root of your application. 6 | 7 | ENGINE_ROOT = File.expand_path('..', __dir__) 8 | ENGINE_PATH = File.expand_path('../lib/lecter/engine', __dir__) 9 | APP_PATH = File.expand_path('../test/dummy/config/application', __dir__) 10 | 11 | # Set up gems listed in the Gemfile. 12 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 13 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 14 | 15 | require 'rails/all' 16 | require 'rails/engine/commands' 17 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Exclude: 3 | - 'lib/lecter/html_generator.rb' 4 | - 'lib/lecter/rack.rb' 5 | - 'lib/lecter/trace_point.rb' 6 | - 'vendor/**/*' 7 | Documentation: 8 | Enabled: false 9 | Layout/LineLength: 10 | Max: 100 11 | Lint/RaiseException: 12 | Enabled: true 13 | Lint/StructNewOverride: 14 | Enabled: true 15 | Style/HashEachMethods: 16 | Enabled: true 17 | Style/HashTransformKeys: 18 | Enabled: true 19 | Style/HashTransformValues: 20 | Enabled: true 21 | Metrics/BlockLength: 22 | ExcludedMethods: ['describe', 'context'] 23 | Layout/ArrayAlignment: 24 | EnforcedStyle: with_fixed_indentation 25 | IndentationWidth: 2 26 | -------------------------------------------------------------------------------- /lib/lecter/formatter_headers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Lecter 4 | class FormatterHeaders 5 | WRONG_HEADERS_MSG = 'Wrong headers' 6 | attr_reader :result, :error_message 7 | 8 | def initialize(headers) 9 | @dirty_headers = headers 10 | end 11 | 12 | def call 13 | @result = dirty_headers 14 | .split(',') 15 | .map { |header_with_value| header_with_value.split('=') } 16 | .to_h 17 | rescue StandardError 18 | @error_message = WRONG_HEADERS_MSG 19 | false 20 | end 21 | 22 | private 23 | 24 | attr_accessor :dirty_headers 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "pineapplethief", 10 | "name": "Aleksey Glukhov", 11 | "avatar_url": "https://avatars1.githubusercontent.com/u/4012690?v=4", 12 | "profile": "https://github.com/pineapplethief", 13 | "contributions": [ 14 | "code", 15 | "doc", 16 | "maintenance" 17 | ] 18 | } 19 | ], 20 | "contributorsPerLine": 7, 21 | "projectName": "lecter", 22 | "projectOwner": "Neodelf", 23 | "repoType": "github", 24 | "repoHost": "https://github.com", 25 | "skipCi": true 26 | } 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report an issue with Lecter you've found. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Actual behavior** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See an error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem, please. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /spec/formatter_payload_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | RSpec.describe Lecter::FormatterPayload, type: :service do 6 | let(:payload) { '"post"=>{"title"=>"New title", "description"=>"Desciption"}' } 7 | 8 | subject { described_class.new(payload).call } 9 | 10 | it 'returns hash' do 11 | is_expected.to eq( 12 | { 13 | lecter_enabled: true, 14 | 'post' => { 'description' => 'Desciption', 'title' => 'New title' } 15 | } 16 | ) 17 | end 18 | 19 | context 'if payload is empty string' do 20 | let(:payload) { '' } 21 | 22 | it 'returns hash' do 23 | is_expected.to eq({ lecter_enabled: true }) 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest new Lecter features or improvements to existing features. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Replace this text with a summary of the changes in your PR. 2 | The more detailed you are, the better.** 3 | 4 | ----------------- 5 | 6 | Before submitting the PR make sure the following are checked: 7 | 8 | * [ ] The PR relates to *only* one subject with a clear title and description in grammatically correct, complete sentences. 9 | * [ ] Wrote [good commit messages][1]. 10 | * [ ] Commit message starts with `[Fix #issue-number]` (if the related issue exists). 11 | * [ ] Feature branch is up-to-date with `master` (if not - rebase it). 12 | * [ ] Squashed related commits together. 13 | * [ ] Added tests. 14 | * [ ] Ran `bundle exec rake default`. It executes all tests and runs RuboCop on its own code. 15 | 16 | [1]: https://chris.beams.io/posts/git-commit/ 17 | -------------------------------------------------------------------------------- /lib/lecter/formatter_payload.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'json' 4 | 5 | module Lecter 6 | class FormatterPayload 7 | WRONG_PARAMETERS_MSG = 'Wrong parameters' 8 | attr_reader :result, :error_message 9 | 10 | def initialize(payload) 11 | @dirty_payload = payload 12 | end 13 | 14 | def call 15 | @result = json_parse(dirty_payload).merge(lecter_enabled_parameter) 16 | rescue JSON::ParserError 17 | @error_message = WRONG_PARAMETERS_MSG 18 | false 19 | end 20 | 21 | private 22 | 23 | attr_accessor :dirty_payload 24 | 25 | def json_parse(string) 26 | string = '{' + string + '}' unless string.match(/\A{.*}\z/) 27 | string.gsub!('=>', ':')&.gsub!(/(“|”)/, '"') 28 | JSON.parse(string) 29 | end 30 | 31 | def lecter_enabled_parameter 32 | { lecter_enabled: true } 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/lecter/rack.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Lecter 4 | class Rack 5 | def initialize(app) 6 | @app = app 7 | @tp = Lecter::TracePoint.new.build 8 | end 9 | 10 | def call(env) 11 | if ::Rack::Request.new(env).params['lecter_enabled'] 12 | thread = Thread.current 13 | thread[:items] = '' 14 | tp.enable 15 | ActionController::Base.allow_forgery_protection = false 16 | end 17 | 18 | status, headers, response = @app.call(env) 19 | 20 | if tp.enabled? 21 | response = [status.to_s + thread[:items]] 22 | status = 200 23 | headers = {} 24 | end 25 | 26 | [status, headers, response] 27 | ensure 28 | if tp.enabled? 29 | tp.disable 30 | ActionController::Base.allow_forgery_protection = true 31 | Thread.current[:items] = nil 32 | end 33 | end 34 | 35 | private 36 | 37 | attr_reader :tp 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/lecter/trace_point.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Lecter 4 | class TracePoint 5 | def build 6 | tp = ::TracePoint.new(:line, :class, :call, :c_call, :return) do |trace_point| 7 | if trace_point.path&.exclude?('/app/views') && 8 | trace_point.path&.exclude?('/app/helpers') && 9 | trace_point.path&.include?(Rails.root.to_s) && 10 | trace_point.method_id != :method_added && 11 | trace_point.defined_class != Module && 12 | trace_point.defined_class != Class && 13 | trace_point.defined_class != String && 14 | trace_point.defined_class != Kernel && 15 | trace_point.defined_class != NilClass 16 | 17 | Thread.current[:items] += [ 18 | trace_point.path, 19 | trace_point.lineno, 20 | trace_point.defined_class, 21 | trace_point.method_id, 22 | trace_point.event 23 | ].join(' ') + ';' 24 | end 25 | end 26 | 27 | tp 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/html_generator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | RSpec.describe Lecter::HtmlGenerator, type: :service do 6 | let(:data) do 7 | [{ 'spec/fixtures/source.rb' => [3] }] 8 | end 9 | 10 | subject { described_class.new(data).call } 11 | 12 | it 'returns array of Lecter::FileListing objects' do 13 | all_elements_has_specific_class = subject.inject(true) do |memo, object| 14 | memo && object.is_a?(Lecter::FileListing) 15 | end 16 | 17 | expect(all_elements_has_specific_class).to be_truthy 18 | end 19 | 20 | it 'returns file_path object params' do 21 | expect(subject.first.file_path) 22 | .to eq('spec/fixtures/source.rb') 23 | end 24 | 25 | it 'returns value object params' do 26 | expect(subject.first.html_rows).to eq( 27 | [ 28 | "
1 # frozen_string_literal: true \n
", 29 | "
2 \n
", 30 | "
3 some ruby code -> 1\n
" 31 | ] 32 | ) 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Neodelf 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/lecter/html_row.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Lecter 4 | class HtmlRow 5 | ARROW = '-> ' 6 | BACKGROUND_INCLUDED_ROW = '#4a4a4a' 7 | NEW_LINE_SYMBOL = "\n" 8 | 9 | def initialize(row, row_number, row_executable, order_of_executed_lines) 10 | @row = row 11 | @row_number = row_number 12 | @row_executable = row_executable 13 | @order_of_executed_lines = order_of_executed_lines 14 | end 15 | 16 | def create 17 | "
#{html_row}
" 18 | end 19 | 20 | private 21 | 22 | attr_reader :row, :row_number, :row_executable, :order_of_executed_lines 23 | 24 | def html_row 25 | [row_number, row, row_calling_order_number].join(' ') + NEW_LINE_SYMBOL 26 | end 27 | 28 | def row_calling_order_number 29 | return unless row_executable 30 | 31 | ARROW + order_of_executed_lines 32 | .each_with_index 33 | .select { |_, index| order_of_executed_lines[index] == row_number } 34 | .map { |_, index| index + 1 } 35 | .join(', ') 36 | end 37 | 38 | def style 39 | row_executable ? "background-color: #{BACKGROUND_INCLUDED_ROW};" : nil 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /app/controllers/lecter/diagnosis_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Lecter 4 | class DiagnosisController < ActionController::Base 5 | layout 'lecter' 6 | before_action :format_request_data, only: :create 7 | 8 | def new; end 9 | 10 | def show 11 | redirect_to new_diagnosis_path 12 | end 13 | 14 | def create 15 | requester = Lecter::Requester.new(requester_params) 16 | if requester.call 17 | @file_listings = HtmlGenerator.new(requester.lines).call 18 | render :show 19 | else 20 | flash[:error] = requester.error_message 21 | render :new 22 | end 23 | end 24 | 25 | private 26 | 27 | def diagnosis_params 28 | params.permit(:endpoint, :body_raw, :method, :headers) 29 | end 30 | 31 | def requester_params 32 | { 33 | method: diagnosis_params[:method].downcase.to_sym, 34 | url: diagnosis_params[:endpoint], 35 | payload: formatter_payload.result, 36 | headers: formatted_headers.result 37 | } 38 | end 39 | 40 | def format_request_data 41 | formatters = [formatter_payload, formatted_headers] 42 | return if formatters.all?(&:call) 43 | 44 | flash[:error] = formatters.map(&:error_message).join(', ') 45 | render :new 46 | end 47 | 48 | def formatter_payload 49 | @formatter_payload ||= Lecter::FormatterPayload.new(diagnosis_params[:body_raw]) 50 | end 51 | 52 | def formatted_headers 53 | @formatted_headers ||= Lecter::FormatterHeaders.new(diagnosis_params[:headers]) 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/lecter/requester.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Lecter 4 | class Requester 5 | WRONG_URL_MSG = 'Wrong url' 6 | 7 | attr_reader :lines, :error_message 8 | 9 | def initialize(params) 10 | @method = params[:method] 11 | @url = params[:url] 12 | @payload = params[:payload] 13 | @lines = [] 14 | @headers = params[:headers] 15 | end 16 | 17 | def call 18 | return false unless response 19 | 20 | prepare_lines 21 | rescue URI::InvalidURIError 22 | @error_message = WRONG_URL_MSG 23 | false 24 | rescue RestClient::ExceptionWithResponse => e 25 | @error_message = e.message 26 | false 27 | end 28 | 29 | private 30 | 31 | attr_accessor :method, :url, :payload, :headers 32 | 33 | def prepare_lines 34 | items.each do |item| 35 | file, line_number = item.split(' ') 36 | line_number = line_number.to_i 37 | 38 | if line_belongs_to_last?(file) 39 | lines.last[file] = lines.last[file] << line_number 40 | else 41 | lines << { file.to_s => [line_number] } 42 | end 43 | end 44 | end 45 | 46 | def response 47 | @response ||= RestClient::Request.execute( 48 | method: method, 49 | url: url, 50 | payload: payload, 51 | headers: headers 52 | ) 53 | end 54 | 55 | def items 56 | @items ||= response.body[3..-1].split(';') 57 | end 58 | 59 | def line_belongs_to_last?(file) 60 | lines.last.is_a?(Hash) && lines.last.keys.first.to_s == file 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | If you discover issues, have ideas for improvements or new features, 4 | please report them to the [issue tracker][1] of the repository or 5 | submit a pull request. Please, try to follow these guidelines when you 6 | do so. 7 | 8 | ## Issue reporting 9 | 10 | * Check that the issue has not already been reported. 11 | * Check that the issue has not already been fixed in the latest code 12 | (a.k.a. `master`). 13 | * Be clear, concise and precise in your description of the problem. 14 | * Open an issue with a descriptive title and a summary in grammatically correct, 15 | complete sentences. 16 | 17 | * Include any relevant code to the issue summary. 18 | 19 | ## Pull requests 20 | 21 | * Read [how to properly contribute to open source projects on GitHub][1]. 22 | * Fork the project. 23 | * Use a topic/feature branch to easily amend a pull request later, if necessary. 24 | * Write [good commit messages][2]. 25 | * Use the same coding conventions as the rest of the project. 26 | * Commit and push until you are happy with your contribution. 27 | * If your change has a corresponding open GitHub issue, prefix the commit message with `[Fix #github-issue-number]`. 28 | * Make sure to add tests for it. This is important so I don't break it 29 | in a future version unintentionally. 30 | * Open a [pull request][3] that relates to *only* one subject with a clear title 31 | and description in grammatically correct, complete sentences. 32 | 33 | [1]: https://www.gun.io/blog/how-to-github-fork-branch-and-pull-request 34 | [2]: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 35 | [3]: https://help.github.com/articles/about-pull-requests 36 | -------------------------------------------------------------------------------- /lecter.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | lib = File.expand_path('lib', __dir__) 4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 5 | require 'lecter/version' 6 | 7 | Gem::Specification.new do |spec| 8 | spec.name = 'lecter' 9 | spec.version = Lecter::VERSION 10 | spec.authors = ['Neodelf'] 11 | spec.email = ['neodelf@gmail.com'] 12 | 13 | spec.summary = 'Show executable code by request.' 14 | spec.description = 'The main purpose of that gem is helping developers to understand which '\ 15 | 'code executes by request.
' 16 | spec.homepage = 'https://github.com/neodelf/lecter' 17 | spec.license = 'MIT' 18 | 19 | spec.metadata['allowed_push_host'] = 'https://rubygems.org' 20 | 21 | spec.metadata['homepage_uri'] = spec.homepage 22 | # spec.metadata["source_code_uri"] = "Put your gem's public repo URL here." 23 | # spec.metadata["changelog_uri"] = "Put your gem's CHANGELOG.md URL here." 24 | 25 | # Specify which files should be added to the gem when it is released. 26 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 27 | spec.files = Dir.chdir(File.expand_path(__dir__)) do 28 | `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 29 | end 30 | spec.bindir = 'exe' 31 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 32 | spec.require_paths = ['lib'] 33 | 34 | spec.add_development_dependency 'bundler', '~> 2.0' 35 | spec.add_development_dependency 'rake', '~> 13.0' 36 | spec.add_development_dependency 'rspec', '~> 3.0' 37 | spec.add_development_dependency 'rubocop' 38 | 39 | spec.add_runtime_dependency 'rest-client' 40 | spec.add_runtime_dependency 'simplecov', '0.17.1' 41 | end 42 | -------------------------------------------------------------------------------- /spec/html_row_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | RSpec.describe Lecter::HtmlRow, type: :service do 6 | let(:row) { 'some code in ruby' } 7 | let(:row_number) { 1 } 8 | let(:row_executable) { true } 9 | let(:order_of_executed_lines) { [1] } 10 | 11 | subject { described_class.new(row, row_number, row_executable, order_of_executed_lines).create } 12 | 13 | context 'if row is executable' do 14 | it 'adds executable order to the end of row' do 15 | is_expected.to end_with( 16 | "
1 some \ 17 | code in ruby -> 1\n
" 18 | ) 19 | end 20 | 21 | context 'if row is executed two times' do 22 | let(:order_of_executed_lines) { [1, 1] } 23 | 24 | it 'adds executable order to the end of row' do 25 | is_expected.to end_with("-> 1, 2\n") 26 | end 27 | end 28 | 29 | context 'if executing row is not consistently' do 30 | let(:order_of_executed_lines) { [1, 2, 1] } 31 | 32 | it 'adds executable order to the end of row' do 33 | is_expected.to end_with("-> 1, 3\n") 34 | end 35 | end 36 | 37 | context 'if executing row is last in order' do 38 | let(:order_of_executed_lines) { [2, 1] } 39 | 40 | it 'adds executable order to the end of row' do 41 | is_expected.to end_with("-> 2\n") 42 | end 43 | end 44 | end 45 | 46 | context 'if row is not executable' do 47 | let(:row_executable) { false } 48 | 49 | it 'adds background color' do 50 | is_expected.not_to include("background-color: #{described_class::BACKGROUND_INCLUDED_ROW}") 51 | end 52 | 53 | it 'adds arrow' do 54 | is_expected.not_to include(described_class::ARROW) 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/lecter/html_generator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Lecter 4 | class HtmlGenerator 5 | COUNT_LINES_AROUND_RUNNING_ROW = 5 6 | ELLIPSIS = '...' 7 | NEW_LINE = "\n" 8 | 9 | def initialize(data) 10 | @data = data 11 | end 12 | 13 | def call 14 | @data.each.map do |item| 15 | @file_path = item.keys.first 16 | @executable_row_numbers = item.values.flatten 17 | previous_row_is_empty = false 18 | 19 | html_rows = file_context.each_with_index.map do |file_row, file_row_index| 20 | @file_row_index = file_row_index 21 | row_executable = executable_row_numbers.include?(file_row_index + 1) 22 | 23 | if row_executable || file_row_in_showing_range?(file_row_index) 24 | previous_row_is_empty = false 25 | Lecter::HtmlRow.new( 26 | file_row, 27 | file_row_index + 1, 28 | row_executable, 29 | executable_row_numbers 30 | ).create 31 | elsif !previous_row_is_empty 32 | previous_row_is_empty = true 33 | ELLIPSIS + NEW_LINE 34 | end 35 | end 36 | 37 | FileListing.new(file_path, html_rows) 38 | end 39 | end 40 | 41 | private 42 | 43 | attr_accessor :executable_row_numbers, :file_row_index, :file_path 44 | 45 | def file_row_in_showing_range?(_index) 46 | executable_row_numbers.reduce(false) do |memo, row_number| 47 | memo || 48 | (row_number - COUNT_LINES_AROUND_RUNNING_ROW - 1.. 49 | row_number + COUNT_LINES_AROUND_RUNNING_ROW - 1).include?(file_row_index) 50 | end 51 | end 52 | 53 | def file_context 54 | File.open(file_path, 'r').read.split(NEW_LINE) 55 | end 56 | end 57 | 58 | class FileListing 59 | attr_reader :file_path, :html_rows 60 | 61 | def initialize(file_path, html_rows) 62 | @file_path = file_path 63 | @html_rows = html_rows 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | lecter (0.2.0) 5 | rest-client 6 | simplecov (= 0.17.1) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | ast (2.4.0) 12 | diff-lcs (1.3) 13 | docile (1.4.0) 14 | domain_name (0.5.20190701) 15 | unf (>= 0.0.5, < 1.0.0) 16 | http-accept (1.7.0) 17 | http-cookie (1.0.4) 18 | domain_name (~> 0.5) 19 | jaro_winkler (1.5.4) 20 | json (2.3.0) 21 | mime-types (3.4.1) 22 | mime-types-data (~> 3.2015) 23 | mime-types-data (3.2022.0105) 24 | netrc (0.11.0) 25 | parallel (1.19.1) 26 | parser (2.7.1.0) 27 | ast (~> 2.4.0) 28 | rainbow (3.0.0) 29 | rake (13.0.1) 30 | rest-client (2.1.0) 31 | http-accept (>= 1.7.0, < 2.0) 32 | http-cookie (>= 1.0.2, < 2.0) 33 | mime-types (>= 1.16, < 4.0) 34 | netrc (~> 0.8) 35 | rexml (3.2.5) 36 | rspec (3.8.0) 37 | rspec-core (~> 3.8.0) 38 | rspec-expectations (~> 3.8.0) 39 | rspec-mocks (~> 3.8.0) 40 | rspec-core (3.8.2) 41 | rspec-support (~> 3.8.0) 42 | rspec-expectations (3.8.4) 43 | diff-lcs (>= 1.2.0, < 2.0) 44 | rspec-support (~> 3.8.0) 45 | rspec-mocks (3.8.1) 46 | diff-lcs (>= 1.2.0, < 2.0) 47 | rspec-support (~> 3.8.0) 48 | rspec-support (3.8.2) 49 | rubocop (0.81.0) 50 | jaro_winkler (~> 1.5.1) 51 | parallel (~> 1.10) 52 | parser (>= 2.7.0.1) 53 | rainbow (>= 2.2.2, < 4.0) 54 | rexml 55 | ruby-progressbar (~> 1.7) 56 | unicode-display_width (>= 1.4.0, < 2.0) 57 | ruby-progressbar (1.10.1) 58 | simplecov (0.17.1) 59 | docile (~> 1.1) 60 | json (>= 1.8, < 3) 61 | simplecov-html (~> 0.10.0) 62 | simplecov-html (0.10.2) 63 | unf (0.1.4) 64 | unf_ext 65 | unf_ext (0.0.8) 66 | unicode-display_width (1.7.0) 67 | 68 | PLATFORMS 69 | ruby 70 | 71 | DEPENDENCIES 72 | bundler (~> 2.0) 73 | lecter! 74 | rake (~> 13.0) 75 | rspec (~> 3.0) 76 | rubocop 77 | 78 | BUNDLED WITH 79 | 2.1.2 80 | -------------------------------------------------------------------------------- /app/views/lecter/diagnosis/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :title, t('.title') %> 2 | 3 |
4 |

<%= t('.title') %>

5 | 6 | <%= form_tag diagnosis_path do %> 7 | <% flash.each do |key, value| %> 8 |
9 | <%= value %> 10 |
11 | <% end %> 12 |
13 | <%= label_tag t('.method'), nil, class: 'col-sm-2 col-form-label' %> 14 |
15 | <%= select_tag :method, options_for_select(Lecter::AVAILABLE_METHODS, Lecter::DEFAULT_METHOD) %> 16 |
17 |
18 | Choose one 19 |
20 |
21 |
22 | <%= label_tag t('.endpoint'), nil, class: 'col-sm-2 col-form-label' %> 23 |
24 | <%= text_field_tag :endpoint, nil, class: 'form-control', placeholder: 'Endpoint' %> 25 |
26 |
27 | localhost:3000/posts 28 |
29 |
30 |
31 | <%= label_tag t('.headers'), nil, class: 'col-sm-2 col-form-label' %> 32 |
33 | <%= text_field_tag :headers, nil, class: 'form-control', placeholder: 'Headers' %> 34 |
35 |
36 | content_type=text/html,accept=*/* 37 |
38 |
39 |
40 | <%= label_tag t('.body_raw'), nil, class: 'col-sm-2 col-form-label' %> 41 |
42 | <%= text_area_tag :body_raw, nil, rows: 10, class: 'form-control', placeholder: 'Body(raw)' %> 43 |
44 |
45 | "post" => { 46 | "title" => "New title", 47 | "description" => "Desciption" 48 | } 49 |
50 |
51 | 52 |

53 | <%= submit_tag t('.send'), class: 'btn btn-primary' %> 54 | <% end %> 55 |
56 | 57 | -------------------------------------------------------------------------------- /spec/requester_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | RSpec.describe Lecter::Requester do 6 | let(:specific_parameter) { { lecter_enabled: true } } 7 | let(:url) { 'localhost:3009/posts' } 8 | let(:payload) { { 'post' => { 'title' => 'New title', 'description' => 'New description' } } } 9 | let(:headers) { { content_type: 'text/html', accept: '*/*' } } 10 | let(:params) do 11 | { method: :anything, url: url, payload: payload, lecter_enabled: true, headers: headers } 12 | end 13 | let(:instance) { described_class.new(params) } 14 | 15 | subject { instance.call } 16 | 17 | context 'if all params are valid' do 18 | before do 19 | response = instance_double( 20 | RestClient::Response, 21 | body: "\ 22 | 302/absolute_path_to_app/app/controllers/posts_controller.rb 26 PostsController create call;\ 23 | /absolute_path_to_app/app/controllers/posts_controller.rb 27 PostsController create line;\ 24 | /absolute_path_to_app/app/controllers/posts_controller.rb 71 PostsController post_params call;\ 25 | /absolute_path_to_app/app/controllers/posts_controller.rb 72 PostsController post_params line;/\ 26 | absolute_path_to_app/app/controllers/posts_controller.rb 73 PostsController post_params return;/\ 27 | absolute_path_to_app/app/controllers/posts_controller.rb 29 PostsController create line;\ 28 | /absolute_path_to_app/app/controllers/posts_controller.rb 30 PostsController create line;\ 29 | /absolute_path_to_app/app/controllers/posts_controller.rb 31 PostsController create line;\ 30 | /absolute_path_to_app/app/controllers/posts_controller.rb 32 PostsController create line;\ 31 | /absolute_path_to_app/app/controllers/posts_controller.rb 31 PostsController create line;\ 32 | /absolute_path_to_app/app/controllers/posts_controller.rb 38 PostsController create return;" 33 | ) 34 | allow(RestClient::Request).to receive(:execute).and_return(response) 35 | subject 36 | end 37 | 38 | it 'generate lines as hash in right format' do 39 | expect(instance.lines).to eq( 40 | [ 41 | { 42 | '/absolute_path_to_app/app/controllers/posts_controller.rb' => 43 | [26, 27, 71, 72, 73, 29, 30, 31, 32, 31, 38] 44 | } 45 | ] 46 | ) 47 | end 48 | end 49 | 50 | [RestClient::ExceptionWithResponse, URI::InvalidURIError].each do |error| 51 | context "if raise #{error}" do 52 | before do 53 | allow(RestClient::Request).to receive(:execute).and_raise(error) 54 | end 55 | 56 | it 'returns false' do 57 | is_expected.to be_falsey 58 | end 59 | 60 | it 'make :lines nil' do 61 | expect(instance.lines).to be_empty 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | working_directory: ~/bikeindex/bike_index 5 | environment: 6 | COVERAGE: true 7 | docker: 8 | - image: circleci/ruby:2.6.3 9 | steps: 10 | - checkout 11 | - restore_cache: 12 | keys: 13 | # This branch if available 14 | - v2-dep-{{ .Branch }}- 15 | # Default branch if not 16 | - v2-dep-master- 17 | # Any branch if there are none on the default branch - this should be unnecessary if you have your default branch configured correctly 18 | - v2-dep- 19 | - run: 20 | name: Install Bundler 21 | command: gem install bundler -v 2.1.2 22 | - run: 23 | name: Bundle Gems 24 | command: bundle install --path=vendor/bundle --jobs=4 --retry=3 25 | - run: 26 | name: Rubocop 27 | command: bundle exec rubocop 28 | - run: 29 | name: Install Code Climate Test Reporter 30 | command: | 31 | curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 32 | chmod +x ./cc-test-reporter 33 | - save_cache: 34 | key: v2-dep-{{ .Branch }}-{{ epoch }} 35 | paths: 36 | - ./vendor/bundle 37 | - ~/.bundle 38 | - run: 39 | name: Run tests 40 | command: | 41 | ./cc-test-reporter before-build 42 | TESTFILES=$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings) 43 | bundle exec rspec --profile 10 \ 44 | --color \ 45 | --order random \ 46 | --format progress \ 47 | -- ${TESTFILES} 48 | - run: 49 | name: Code Climate Test Coverage 50 | command: | 51 | ./cc-test-reporter format-coverage -t simplecov -o "coverage/codeclimate.$CIRCLE_NODE_INDEX.json" 52 | - persist_to_workspace: 53 | root: coverage 54 | paths: 55 | - codeclimate.*.json 56 | 57 | upload-coverage: 58 | docker: 59 | - image: circleci/ruby:2.6.3 60 | environment: 61 | CC_TEST_REPORTER_ID: 9ef6750a0b374973f31c70ccf4bc6be1a4c93b0f2d3537d16e77450ef01f7813 62 | working_directory: ~/bikeindex/bike_index 63 | 64 | steps: 65 | - attach_workspace: 66 | at: ~/bikeindex/bike_index 67 | - run: 68 | name: Install Code Climate Test Reporter 69 | command: | 70 | curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 71 | chmod +x ./cc-test-reporter 72 | - run: 73 | name: Upload Test Reporter To Codeclimate 74 | command: | 75 | ./cc-test-reporter sum-coverage codeclimate.*.json --output codeclimate.total.json 76 | ./cc-test-reporter upload-coverage --input codeclimate.total.json 77 | 78 | workflows: 79 | version: 2 80 | 81 | commit: 82 | jobs: 83 | - build 84 | - upload-coverage: 85 | requires: 86 | - build 87 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at neodelf@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /app/views/layouts/lecter.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= yield(:title) %> 4 | <%= javascript_include_tag '//code.jquery.com/jquery-1.12.4.js', '//code.jquery.com/ui/1.12.1/jquery-ui.js' %> 5 | <%= javascript_include_tag 'https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js' %> 6 | <%= stylesheet_link_tag 'https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css' %> 7 | 8 | 9 | 252 | 253 | 254 |
255 | <%= yield %> 256 |
257 | 258 | 259 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | Lecter Logo 3 | 4 |

Lecter shows an executed code by a URL request.
5 | Debug an unknown code in SECONDS! 6 |

7 | 8 | [![Gem Version][0]][1] 9 | [![Ruby Style Guide][15]][16] 10 | [![CircleCI][2]][3] 11 | [![Test Coverage][12]][13] 12 | [![Maintainability][10]][11] 13 | [![All Contributors](https://img.shields.io/badge/all_contributors-5-green.svg)](#contributors-) 14 |
15 | 16 | > Quid pro quo. I tell you things, you tell me things. Not about this case, though. About yourself.
17 | > -- Hannibal Lecter / The Silence of the Lambs 18 | 19 | ---------- 20 | 21 | ## Table of contents 22 | * [:rotating_light: Problems](#rotating_light-problems) 23 | * [:rocket: Solutions](#rocket-solutions) 24 | * [:bangbang: Requirements](#bangbang-requirements) 25 | * [:hammer_and_wrench: Installation](#hammer_and_wrench-installation) 26 | * [:joystick: Usage](#joystick-usage) 27 | * [:man_technologist: Examples](#man_technologist-examples) 28 | * [:heart: Sponsors](#card_file_box-license) 29 | * [:desktop_computer: Contributing & Contributors](#desktop_computer-contributing--contributors) 30 | * [:card_file_box: License](#card_file_box-license) 31 | 32 | ## :rotating_light: Problems 33 | 1. Developers **don't know** how their app works. 34 | 2. Reading code is a **long** process. 35 | 3. Memorizing many lines of code is **difficult**. 36 | 37 | ## :rocket: Solutions 38 | 1. The main purpose of this gem is **to help developers understand which code is executed** by a request.
39 | 40 | 2. Gem's purpose could be compared to visiting a doctor: 41 | You give the symptoms of your disease and **receive a diagnosis**.
42 | You give the parameters of a request and **receive executable code**. 43 | 44 | 3. You don't have to work with debuggers, read and remember many lines of code.
45 | Just **give & receive**! 46 | 47 | ## :bangbang: Requirements 48 | Please **use a multithreading server** such as a [puma][4] server with more than `1` worker.
49 | For more information about how to tune a puma server, please visit this [link][5]. 50 | 51 | ## :hammer_and_wrench: Installation 52 | Installing is a **very simple** process. 53 | 54 | 1. **Add** the gem to your Gemfile: 55 | 56 | ```ruby 57 | gem 'lecter' 58 | ``` 59 | 60 | 2. **Execute** in a terminal in your app's directory: 61 | 62 | ```zsh 63 | bundle install 64 | ``` 65 | 66 | 3. **Add** routes to your `config/routes.rb`: 67 | ```ruby 68 | mount Lecter::Engine => '/lecter' if Rails.env.development? 69 | ``` 70 | 71 | ## :joystick: Usage 72 | Follow **two** simple steps: 73 | 74 | 1. **Go** to `/lecter` 75 | 76 | 2. **Fill in** the form fields with request parameters: 77 | 78 | `HTTP method` - request's method
79 | `Endpoint` - use absolute route like `localhost:3000/blogs`
80 | `Headers` - request's headers 81 | `Body(raw)` - request's parameters 82 | 83 | ## :man_technologist: Examples 84 | Here is an example with a **POST** request: 85 | 86 | 1. **Go** to `localhost:3000/lecter` 87 | 88 | 2. **Fill** in the form with: 89 | 90 | `HTTP method` - `POST`
91 | `Endpoint` - `localhost:3000/posts`
92 | `Headers` - `content_type=text/html,accept=*/*`
93 | `Body(raw)` - `"post"=>{"title"=>"New title", "description"=>"Desciption"}` 94 | 95 | lecter example post form 96 | 97 | 3. **Submit** the form 98 | 4. **Review** the result 99 | 100 | lecter post form result 101 | 102 | ## :heart: Sponsors 103 | [Evrone Logo][14] 104 | 105 | ## :desktop_computer: Contributing & Contributors 106 | 1. Bug reports and pull requests **are welcome**. 107 | 2. There are many issues as a proposal to improve this library. If you have any ideas, please **feel free** to write your thoughts in a [new issue][7]. 108 | 3. Choose **what you like** to fix or improve on the [issues list][8]. You can ask any questions in the comments. 109 | 4. :bangbang: **Mention of each contributor** will be on the README file. 110 | 111 | **Thanks** goes to these wonderful people: 112 | 113 | 114 | 115 | 116 | 117 | 127 | 136 | 145 | 154 | 155 |
118 | 119 | 120 |
121 | Aleksey Glukhov 122 |
123 |
124 | 💻 125 | 📖 126 |
128 | 129 | 130 |
131 | Alisha Taylor 132 |
133 |
134 | 📖 135 |
137 | 138 | 139 |
140 | Vitaliy Emeliyantsev 141 |
142 |
143 | 📖 144 |
146 | 147 | 148 |
149 | Oskar Janusz 150 |
151 |
152 | 💻 153 |
156 | 157 | 158 | 159 | ## :card_file_box: License 160 | The gem is available as open source under the terms of the [MIT License][9]. 161 | 162 | [0]: https://badge.fury.io/rb/lecter.svg 163 | [1]: https://badge.fury.io/rb/lecter 164 | [2]: https://circleci.com/gh/Neodelf/lecter.svg?style=shield 165 | [3]: https://circleci.com/gh/neodelf/lecter 166 | [4]: https://github.com/puma/puma 167 | [5]: https://github.com/puma/puma#clustered-mode 168 | [7]: https://github.com/Neodelf/lecter/issues/new 169 | [8]: https://github.com/Neodelf/lecter/issues 170 | [9]: https://opensource.org/licenses/MIT 171 | [10]: https://api.codeclimate.com/v1/badges/45d57f439d66990490f1/maintainability 172 | [11]: https://codeclimate.com/github/Neodelf/lecter/maintainability 173 | [12]: https://api.codeclimate.com/v1/badges/45d57f439d66990490f1/test_coverage 174 | [13]: https://codeclimate.com/github/Neodelf/lecter/test_coverage 175 | [14]: https://evrone.com 176 | [15]: https://img.shields.io/badge/code_style-rubocop-brightgreen.svg 177 | [16]: https://rubystyle.guide 178 | -------------------------------------------------------------------------------- /app/views/lecter/diagnosis/show.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :title, t('.title') %> 2 | 3 |
4 |

<%= t('.executed_code') %>

5 |
6 | 7 |
8 |
9 |
10 | 19 |
20 |
21 | 22 |
23 |
24 | <% @file_listings.each_with_index do |file_listing, index| %> 25 |
26 |

27 | 33 |

34 |
35 |
36 |
37 |               <% file_listing.html_rows.each do |html_row| %><%= raw(html_row) %><% end %>
38 |             
39 |
40 |
41 |
42 | <% end %> 43 |
44 |
45 |
46 | 47 | 55 | --------------------------------------------------------------------------------