├── .github └── workflows │ └── ruby.yml ├── .gitignore ├── .hound.yml ├── .overcommit.yml ├── .reek ├── .rubocop.yml ├── Appraisals ├── Gemfile ├── MIT-LICENSE ├── README.md ├── Rakefile ├── gemfiles ├── activerecord_7_0.gemfile ├── activerecord_7_1.gemfile ├── activerecord_7_2.gemfile ├── activerecord_8_0.gemfile ├── rails_7_0.gemfile ├── rails_7_1.gemfile ├── rails_7_2.gemfile └── rails_8_0.gemfile ├── img ├── console.png ├── jetbrains-variant-3.svg └── log.png ├── lib ├── pp_sql.rb └── pp_sql │ └── version.rb ├── pp_sql.gemspec └── test ├── pp_sql_activerecord_test.rb ├── pp_sql_rails_test.rb ├── pp_sql_test.rb └── test_helper.rb /.github/workflows/ruby.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake 6 | # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby 7 | 8 | name: Ruby 9 | 10 | on: 11 | push: 12 | branches: [ "master" ] 13 | pull_request: 14 | branches: [ "master" ] 15 | 16 | permissions: 17 | contents: read 18 | 19 | jobs: 20 | test: 21 | 22 | runs-on: ubuntu-latest 23 | strategy: 24 | matrix: 25 | ruby-version: ['3.1', '3.2', '3.3'] 26 | gemfile: 27 | - gemfiles/rails_7_0.gemfile 28 | - gemfiles/rails_7_1.gemfile 29 | - gemfiles/rails_7_2.gemfile 30 | - gemfiles/rails_8_0.gemfile 31 | - gemfiles/activerecord_7_0.gemfile 32 | - gemfiles/activerecord_7_1.gemfile 33 | - gemfiles/activerecord_7_2.gemfile 34 | - gemfiles/activerecord_8_0.gemfile 35 | exclude: 36 | - ruby-version: '3.1' 37 | gemfile: gemfiles/rails_8_0.gemfile 38 | - ruby-version: '3.1' 39 | gemfile: gemfiles/activerecord_8_0.gemfile 40 | 41 | steps: 42 | - uses: actions/checkout@v3 43 | - name: Set up Ruby 44 | # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, 45 | # change this to (see https://github.com/ruby/setup-ruby#versioning): 46 | uses: ruby/setup-ruby@v1 47 | with: 48 | ruby-version: ${{ matrix.ruby-version }} 49 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically 50 | - name: Install gems 51 | env: 52 | BUNDLE_GEMFILE: ${{ matrix.gemfile }} 53 | run: bundle install 54 | 55 | - name: Run tests 56 | env: 57 | BUNDLE_GEMFILE: ${{ matrix.gemfile }} 58 | run: bundle exec rake 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | log/*.log 3 | Gemfile.lock 4 | *.gem 5 | gemfiles/*.gemfile.lock 6 | gemfiles/.bundle/ 7 | .tool_versions 8 | .ruby-version 9 | -------------------------------------------------------------------------------- /.hound.yml: -------------------------------------------------------------------------------- 1 | rubocop: 2 | config_file: .rubocop.yml 3 | version: 1.22.1 -------------------------------------------------------------------------------- /.overcommit.yml: -------------------------------------------------------------------------------- 1 | # Use this file to configure the Overcommit hooks you wish to use. This will 2 | # extend the default configuration defined in: 3 | # https://github.com/brigade/overcommit/blob/master/config/default.yml 4 | # 5 | # At the topmost level of this YAML file is a key representing type of hook 6 | # being run (e.g. pre-commit, commit-msg, etc.). Within each type you can 7 | # customize each hook, such as whether to only run it on certain files (via 8 | # `include`), whether to only display output if it fails (via `quiet`), etc. 9 | # 10 | # For a complete list of hooks, see: 11 | # https://github.com/brigade/overcommit/tree/master/lib/overcommit/hook 12 | # 13 | # For a complete list of options that you can use to customize hooks, see: 14 | # https://github.com/brigade/overcommit#configuration 15 | # 16 | # Uncomment the following lines to make the configuration take effect. 17 | 18 | PreCommit: 19 | RuboCop: 20 | enabled: true 21 | on_warn: fail # Treat all warnings as failures 22 | AuthorName: 23 | enabled: false 24 | description: 'Check for author name' 25 | 26 | # 27 | # TrailingWhitespace: 28 | # enabled: true 29 | # exclude: 30 | # - '**/db/structure.sql' # Ignore trailing whitespace in generated files 31 | # 32 | #PostCheckout: 33 | # ALL: # Special hook name that customizes all hooks of this type 34 | # quiet: true # Change all post-checkout hooks to only display output on failure 35 | # 36 | # IndexTags: 37 | # enabled: true # Generate a tags file with `ctags` each time HEAD changes 38 | -------------------------------------------------------------------------------- /.reek: -------------------------------------------------------------------------------- 1 | IrresponsibleModule: 2 | enabled: false -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | Layout/LineLength: 2 | Max: 120 3 | Style/Documentation: 4 | Enabled: false 5 | Metrics/CyclomaticComplexity: 6 | Max: 10 7 | Metrics/BlockLength: 8 | Exclude: 9 | - 'test/**/*' 10 | Lint/ConstantDefinitionInBlock: 11 | Exclude: 12 | - 'test/**/*' 13 | AllCops: 14 | Exclude: 15 | - 'gemfiles/**/*' 16 | NewCops: enable 17 | SuggestExtensions: false 18 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # rubocop:disable Security/Eval 4 | skip_gems_snippet = <<~GEMS 5 | group :local_development do 6 | %w[appraisal overcommit pry reek rubocop rails sqlite3].each { |gem_name| remove_gem gem_name } 7 | end 8 | GEMS 9 | 10 | appraise 'rails-7-0' do 11 | gem 'rails', '~> 7.0.0' 12 | gem 'sqlite3', '~> 1.6' 13 | eval(skip_gems_snippet) 14 | end 15 | 16 | appraise 'rails-7-1' do 17 | gem 'rails', '~> 7.1.0' 18 | gem 'sqlite3', '~> 2.0' 19 | eval(skip_gems_snippet) 20 | end 21 | 22 | appraise 'rails-7-2' do 23 | gem 'rails', '~> 7.2.0' 24 | gem 'sqlite3', '~> 2.0' 25 | eval(skip_gems_snippet) 26 | end 27 | 28 | appraise 'rails-8-0' do 29 | gem 'rails', '~> 8.0.0' 30 | gem 'sqlite3', '~> 2.0' 31 | eval(skip_gems_snippet) 32 | end 33 | 34 | appraise 'activerecord-7-0' do 35 | gem 'activerecord', '~> 7.0.0' 36 | gem 'rake' 37 | gem 'sqlite3', '~> 1.6' 38 | eval(skip_gems_snippet) 39 | end 40 | 41 | appraise 'activerecord-7-1' do 42 | gem 'activerecord', '~> 7.1.0' 43 | gem 'rake' 44 | gem 'sqlite3', '~> 2.0' 45 | eval(skip_gems_snippet) 46 | end 47 | 48 | appraise 'activerecord-7-2' do 49 | gem 'activerecord', '~> 7.2.0' 50 | gem 'rake' 51 | gem 'sqlite3', '~> 2.0' 52 | eval(skip_gems_snippet) 53 | end 54 | 55 | appraise 'activerecord-8-0' do 56 | gem 'activerecord', '~> 8.0.0' 57 | gem 'rake' 58 | gem 'sqlite3', '~> 2.0' 59 | eval(skip_gems_snippet) 60 | end 61 | # rubocop:enable Security/Eval 62 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | # Declare your gem's dependencies in pp_sql.gemspec. 6 | # Bundler will treat runtime dependencies like base dependencies, and 7 | # development dependencies will be added by default to the :development group. 8 | gemspec 9 | 10 | # Declare any dependencies that are still in development here instead of in 11 | # your gemspec. These might include edge Rails or gems from your path or 12 | # Git. Remember to move these dependencies to your gemspec before releasing 13 | # your gem to rubygems.org. 14 | 15 | # To use a debugger 16 | # gem 'byebug', group: [:development, :test] 17 | # 18 | 19 | # https://stackoverflow.com/questions/79360526/uninitialized-constant-activesupportloggerthreadsafelevellogger-nameerror 20 | gem 'concurrent-ruby', '1.3.4' 21 | gem 'minitest' 22 | gem 'minitest-focus' 23 | gem 'minitest-reporters' 24 | 25 | group :local_development do 26 | gem 'appraisal' 27 | gem 'overcommit' 28 | gem 'pry' 29 | gem 'rails', '>= 7.0' 30 | gem 'reek' 31 | gem 'rubocop', '~> 1.69.0' 32 | gem 'sqlite3', '>= 1.4' 33 | end 34 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2023 Kvokka 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 | # PpSql 2 | 3 | [![Version ][rubygems_badge]][rubygems] 4 | [![Codacy Badge ][codacy_badge]][codacy] 5 | [![Reviewed by Hound ][hound_badge]][hound] 6 | [](https://api.gitsponsors.com/api/badge/link?p=J9d+7zEGJS+BQMhX3FZDy2lWmzWp75oJZulj80NsvTrAkDEWcEC5PzkwUCDB/oqWGOfl1rwmFSi7crAJTxB6ww==) 7 | 8 | Replace standard `ActiveRecord#to_sql` method with 9 | [`anbt-sql-formatter`][anbt-sql-formatter-link] 10 | gem for pretty SQL code output in console. Rails log will be formatted also. 11 | Example output: 12 | 13 | ![log][log-img] 14 | 15 | Or in console 16 | 17 | ![console][console-img] 18 | 19 | ## Require 20 | 21 | Ruby 3.1+ 22 | 23 | ## Rails 24 | 25 | Rails 7.0+ (optional), will be injected automatically 26 | 27 | ## Legacy 28 | 29 | You can use version `~> 0.2` of this gem with Ruby 2.2, 2.3 and/or Rails 4.0, 4.1 30 | 31 | ## Usage 32 | 33 | ``` 34 | Post.first.to_sql 35 | ``` 36 | 37 | for easy and clean usage with custom string you can use: 38 | 39 | ``` 40 | class MyAwesomeDecoratedString < String 41 | include PpSql::ToSqlBeautify 42 | end 43 | ``` 44 | 45 | ## Installation 46 | 47 | add in Gemfile 48 | 49 | ``` 50 | gem 'pp_sql', group: :development 51 | ``` 52 | 53 | And then execute: 54 | 55 | ```bash 56 | bundle 57 | ``` 58 | 59 | ## With other formatters 60 | 61 | If you are `pry` user, or use custom output formatter, use `puts` for output whitespaces, 62 | like `puts User.all.to_sql`, or use `User.all.pp_sql`. 63 | 64 | ## With Rails 65 | 66 | If you do not want to rewrite default `#to_sql` method you may specify 67 | `PpSql.rewrite_to_sql_method=false` in initializers. 68 | 69 | You can also disable log formatting by specifying `PpSql.add_rails_logger_formatting=false` 70 | in initializers. 71 | 72 | ### Add to Application record 73 | 74 | I found usefull this trick: 75 | 76 | ``` 77 | class ApplicationRecord < ActiveRecord::Base 78 | include PpSql::ToSqlBeautify if defined?(Rails::Console) 79 | 80 | self.abstract_class = true 81 | end 82 | ``` 83 | 84 | ### Supported by 85 | 86 | [![jetbrains][jetbrains-img-link]][jetbrains-link] 87 | 88 | ## Contributing 89 | 90 | Running the tests requires sqlite. To run the tests for different combinations of dependency 91 | versions, run `bundle exec appraisal install` followed by `bundle exec appraisal rake`. 92 | 93 | ## License 94 | 95 | The gem is available as open source under the terms of the 96 | [MIT License][mit-licence-link]. 97 | 98 | [rubygems_badge]: http://img.shields.io/gem/v/pp_sql.svg 99 | [rubygems]: https://rubygems.org/gems/pp_sql 100 | [codacy_badge]: https://app.codacy.com/project/badge/Grade/0394889311ea49529ddea12baac9b699 101 | [codacy]: https://www.codacy.com/gh/kvokka/pp_sql/dashboard?utm_source=github.com&utm_medium=referral&utm_content=kvokka/pp_sql&utm_campaign=Badge_Grade 102 | [hound_badge]: https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg 103 | [hound]: https://houndci.com 104 | 105 | [anbt-sql-formatter-link]: https://github.com/sonota88/anbt-sql-formatter 106 | [mit-licence-link]: http://opensource.org/licenses/MIT 107 | [jetbrains-link]: https://www.jetbrains.com/?from=pp_sql 108 | [jetbrains-img-link]: https://raw.githubusercontent.com/kvokka/pp_sql/master/img/jetbrains-variant-3.svg?sanitize=true 109 | 110 | [log-img]: https://raw.githubusercontent.com/kvokka/pp_sql/master/img/log.png 111 | [console-img]: https://raw.githubusercontent.com/kvokka/pp_sql/master/img/console.png 112 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rubygems' 4 | begin 5 | require 'bundler/setup' 6 | rescue LoadError 7 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 8 | end 9 | 10 | require 'rdoc/task' 11 | 12 | RDoc::Task.new(:rdoc) do |rdoc| 13 | rdoc.rdoc_dir = 'rdoc' 14 | rdoc.title = 'PpSql' 15 | rdoc.options << '--line-numbers' 16 | rdoc.rdoc_files.include('README.md') 17 | rdoc.rdoc_files.include('lib/**/*.rb') 18 | end 19 | 20 | require 'bundler/gem_tasks' 21 | 22 | require 'rake/testtask' 23 | 24 | Rake::TestTask.new(:test) do |t| 25 | t.libs << %w[test lib] 26 | t.pattern = 'test/**/*_test.rb' 27 | t.verbose = false 28 | end 29 | 30 | task default: :test 31 | -------------------------------------------------------------------------------- /gemfiles/activerecord_7_0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "concurrent-ruby", "1.3.4" 6 | gem "minitest" 7 | gem "minitest-focus" 8 | gem "minitest-reporters" 9 | gem "activerecord", "~> 7.0.0" 10 | gem "rake" 11 | gem "sqlite3", "~> 1.6" 12 | 13 | group :local_development do 14 | 15 | end 16 | 17 | gemspec path: "../" 18 | -------------------------------------------------------------------------------- /gemfiles/activerecord_7_1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "concurrent-ruby", "1.3.4" 6 | gem "minitest" 7 | gem "minitest-focus" 8 | gem "minitest-reporters" 9 | gem "activerecord", "~> 7.1.0" 10 | gem "rake" 11 | gem "sqlite3", "~> 2.0" 12 | 13 | group :local_development do 14 | 15 | end 16 | 17 | gemspec path: "../" 18 | -------------------------------------------------------------------------------- /gemfiles/activerecord_7_2.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "concurrent-ruby", "1.3.4" 6 | gem "minitest" 7 | gem "minitest-focus" 8 | gem "minitest-reporters" 9 | gem "activerecord", "~> 7.2.0" 10 | gem "rake" 11 | gem "sqlite3", "~> 2.0" 12 | 13 | group :local_development do 14 | 15 | end 16 | 17 | gemspec path: "../" 18 | -------------------------------------------------------------------------------- /gemfiles/activerecord_8_0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "concurrent-ruby", "1.3.4" 6 | gem "minitest" 7 | gem "minitest-focus" 8 | gem "minitest-reporters" 9 | gem "activerecord", "~> 8.0.0" 10 | gem "rake" 11 | gem "sqlite3", "~> 2.0" 12 | 13 | group :local_development do 14 | 15 | end 16 | 17 | gemspec path: "../" 18 | -------------------------------------------------------------------------------- /gemfiles/rails_7_0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "concurrent-ruby", "1.3.4" 6 | gem "minitest" 7 | gem "minitest-focus" 8 | gem "minitest-reporters" 9 | gem "rails", "~> 7.0.0" 10 | gem "sqlite3", "~> 1.6" 11 | 12 | group :local_development do 13 | 14 | end 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /gemfiles/rails_7_1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "concurrent-ruby", "1.3.4" 6 | gem "minitest" 7 | gem "minitest-focus" 8 | gem "minitest-reporters" 9 | gem "rails", "~> 7.1.0" 10 | gem "sqlite3", "~> 2.0" 11 | 12 | group :local_development do 13 | 14 | end 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /gemfiles/rails_7_2.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "concurrent-ruby", "1.3.4" 6 | gem "minitest" 7 | gem "minitest-focus" 8 | gem "minitest-reporters" 9 | gem "rails", "~> 7.2.0" 10 | gem "sqlite3", "~> 2.0" 11 | 12 | group :local_development do 13 | 14 | end 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /gemfiles/rails_8_0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "concurrent-ruby", "1.3.4" 6 | gem "minitest" 7 | gem "minitest-focus" 8 | gem "minitest-reporters" 9 | gem "rails", "~> 8.0.0" 10 | gem "sqlite3", "~> 2.0" 11 | 12 | group :local_development do 13 | 14 | end 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /img/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvokka/pp_sql/50b7036f2d22ac72b483f447aaea80ffd8058033/img/console.png -------------------------------------------------------------------------------- /img/jetbrains-variant-3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 55 | 56 | 57 | 60 | 62 | 64 | 65 | 66 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /img/log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvokka/pp_sql/50b7036f2d22ac72b483f447aaea80ffd8058033/img/log.png -------------------------------------------------------------------------------- /lib/pp_sql.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module PpSql 4 | WHITE_SPACE = ' ' 5 | 6 | # if you do not want to rewrite AR native method #to_sql 7 | # you may switch this setting to false in initializer 8 | class << self 9 | attr_accessor :rewrite_to_sql_method, :add_rails_logger_formatting 10 | end 11 | self.rewrite_to_sql_method = true 12 | self.add_rails_logger_formatting = true 13 | 14 | module Formatter 15 | private 16 | 17 | def _sql_formatter 18 | return @_sql_formatter if defined?(@_sql_formatter) && @_sql_formatter 19 | 20 | require 'anbt-sql-formatter/formatter' 21 | rule = AnbtSql::Rule.new 22 | rule.keyword = AnbtSql::Rule::KEYWORD_UPPER_CASE 23 | %w[count sum substr date].each { |func_name| rule.function_names << func_name.upcase } 24 | rule.indent_string = WHITE_SPACE 25 | @_sql_formatter = AnbtSql::Formatter.new(rule) 26 | end 27 | end 28 | 29 | module ToSqlBeautify 30 | def to_sql 31 | if ::PpSql.rewrite_to_sql_method 32 | extend Formatter 33 | _sql_formatter.format(defined?(super) ? super.dup : dup) 34 | else 35 | defined?(super) ? super : self 36 | end 37 | end 38 | 39 | def pp_sql 40 | if ::PpSql.rewrite_to_sql_method 41 | puts to_sql 42 | else 43 | extend Formatter 44 | puts _sql_formatter.format(to_sql.dup) 45 | end 46 | end 47 | end 48 | 49 | module LogSubscriberPrettyPrint 50 | include Formatter 51 | 52 | def sql(event) 53 | return super unless ::PpSql.add_rails_logger_formatting 54 | 55 | e = event.dup 56 | e.payload[:sql] = _sql_formatter.format(e.payload[:sql].dup) 57 | super(e) 58 | end 59 | end 60 | 61 | if defined?(::Rails::Railtie) 62 | class Railtie < Rails::Railtie 63 | initializer 'pp_sql.override_to_sql' do 64 | ActiveSupport.on_load(:active_record) do 65 | ActiveRecord::Relation.prepend ToSqlBeautify 66 | ActiveRecord::LogSubscriber.prepend LogSubscriberPrettyPrint 67 | end 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /lib/pp_sql/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module PpSql 4 | VERSION = '2.1.0' 5 | end 6 | -------------------------------------------------------------------------------- /pp_sql.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.push File.expand_path('lib', __dir__) 4 | 5 | # Maintain your gem's version: 6 | require 'pp_sql/version' 7 | 8 | # Describe your gem and declare its dependencies: 9 | Gem::Specification.new do |s| 10 | s.name = 'pp_sql' 11 | s.version = PpSql::VERSION 12 | s.authors = ['Kvokka'] 13 | s.email = ['kvokka@yahoo.com'] 14 | s.homepage = 'https://github.com/kvokka/pp_sql' 15 | s.summary = 'Beautify SQL output of ActiveRecord#to_sql' 16 | s.description = 'Helps to save your eyes, when reading hardcore SQL requests in console' 17 | s.license = 'MIT' 18 | 19 | s.files = Dir['lib/**/*', 'MIT-LICENSE', 'Rakefile', 'README.md'] 20 | 21 | s.required_ruby_version = '>= 3.1.0' 22 | 23 | s.add_dependency 'activerecord' 24 | s.add_dependency 'anbt-sql-formatter', '~> 0.1.0' 25 | 26 | s.metadata['rubygems_mfa_required'] = 'true' 27 | end 28 | -------------------------------------------------------------------------------- /test/pp_sql_activerecord_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'test_helper' 4 | require 'sqlite3' 5 | require 'active_record' 6 | 7 | module ActiveRecord 8 | class Base 9 | establish_connection(adapter: 'sqlite3', database: ':memory:') 10 | connection.create_table(:users) { |t| t.string :name } 11 | self.logger = Logger.new(::LOGGER) 12 | end 13 | 14 | class Relation 15 | prepend PpSql::ToSqlBeautify 16 | end 17 | 18 | class LogSubscriber 19 | prepend PpSql::LogSubscriberPrettyPrint 20 | end 21 | end 22 | 23 | class User < ActiveRecord::Base; end 24 | 25 | describe PpSql do 26 | after { clear_logs! && set_default_config! } 27 | 28 | it 'ActiveRecord logs with formatted output' do 29 | User.create 30 | assert(LOGGER.string.lines.detect { |line| line =~ /INTO\n/ }) 31 | clear_logs! 32 | User.first 33 | assert_equal LOGGER.string.lines.count, 6 34 | end 35 | 36 | it 'ActiveRecord logs with default output' do 37 | PpSql.add_rails_logger_formatting = false 38 | User.create 39 | clear_logs! 40 | User.first 41 | assert_equal LOGGER.string.lines.count, 1 42 | end 43 | 44 | it 'to_sql formats a relation properly' do 45 | User.create 46 | assert_equal User.all.to_sql, "SELECT\n \"users\" . *\n FROM\n \"users\"" 47 | end 48 | 49 | it 'to_sql with default output' do 50 | PpSql.rewrite_to_sql_method = false 51 | User.create 52 | assert_equal User.all.to_sql.lines.count, 1 53 | end 54 | 55 | it 'to_sql with default output but formatted logs' do 56 | PpSql.rewrite_to_sql_method = false 57 | User.create 58 | assert_equal User.all.to_sql.lines.count, 1 59 | clear_logs! 60 | User.first 61 | assert_equal LOGGER.string.lines.count, 6 62 | end 63 | 64 | it 'pp_sql formats a relation properly' do 65 | User.create 66 | out, = capture_io do 67 | User.all.pp_sql 68 | end 69 | assert_equal out, "SELECT\n \"users\" . *\n FROM\n \"users\"\n" 70 | end 71 | 72 | it 'pp_sql formats frozen strings properly' do 73 | PpSql.rewrite_to_sql_method = false 74 | User.create 75 | frozen_clause = 'id > 0' 76 | out, = capture_io do 77 | User.all.where(frozen_clause).pp_sql 78 | end 79 | assert_equal out.lines.count, 8 80 | end 81 | 82 | private 83 | 84 | def clear_logs! 85 | LOGGER.string.clear 86 | end 87 | 88 | def set_default_config! 89 | PpSql.rewrite_to_sql_method = true 90 | PpSql.add_rails_logger_formatting = true 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /test/pp_sql_rails_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | return if Gem::Specification.find_all_by_name('rails').empty? 4 | 5 | require 'rails' 6 | require 'test_helper' 7 | require 'active_record' 8 | require 'sqlite3' 9 | ENV['RAILS_ENV'] = 'test' 10 | 11 | module TeatApp 12 | class Application < Rails::Application 13 | config.eager_load = false 14 | initialize! 15 | end 16 | end 17 | 18 | module ActiveRecord 19 | class Base 20 | establish_connection(adapter: 'sqlite3', database: ':memory:') 21 | connection.create_table(:users) { |t| t.string :name } 22 | self.logger = Logger.new(::LOGGER) 23 | end 24 | end 25 | 26 | class User < ActiveRecord::Base; end 27 | 28 | describe PpSql do 29 | after { clear_logs! && set_default_config! } 30 | 31 | it 'Rails & ActiveRecord with formatted output' do 32 | User.create 33 | assert(LOGGER.string.lines.detect { |line| line =~ /INTO\n/ }) 34 | clear_logs! 35 | User.first 36 | assert_equal LOGGER.string.lines.count, 6 37 | end 38 | 39 | it 'Rails & ActiveRecord with default output' do 40 | PpSql.add_rails_logger_formatting = false 41 | User.create 42 | clear_logs! 43 | User.first 44 | assert_equal LOGGER.string.lines.count, 1 45 | end 46 | 47 | private 48 | 49 | def clear_logs! 50 | LOGGER.string.clear 51 | end 52 | 53 | def set_default_config! 54 | PpSql.add_rails_logger_formatting = true 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /test/pp_sql_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'test_helper' 4 | 5 | describe PpSql do 6 | class SubString < String 7 | include PpSql::ToSqlBeautify 8 | end 9 | 10 | let(:str) { SubString.new 'SELECT COUNT(*) FROM "users"' } 11 | 12 | after { PpSql.rewrite_to_sql_method = true } 13 | 14 | it 'parses provided sql' do 15 | assert_equal str.to_sql.lines.count, 4 16 | end 17 | 18 | it 'returns string as is' do 19 | PpSql.rewrite_to_sql_method = false 20 | assert_equal str.to_sql.lines.count, 1 21 | end 22 | 23 | it 'formats and prints with pp_sql' do 24 | out, = capture_io do 25 | str.pp_sql 26 | end 27 | 28 | assert_equal out, "SELECT\n COUNT( * )\n FROM\n \"users\"\n" 29 | end 30 | 31 | it 'formats and prints with pp_sql if rewrite_to_sql_method is false' do 32 | PpSql.rewrite_to_sql_method = false 33 | out, = capture_io do 34 | str.pp_sql 35 | end 36 | 37 | assert_equal out, "SELECT\n COUNT( * )\n FROM\n \"users\"\n" 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'minitest/autorun' 4 | require 'minitest/focus' 5 | require 'pp_sql' 6 | 7 | require 'minitest/reporters' 8 | 9 | LOGGER = StringIO.new 10 | 11 | Minitest::Reporters.use! 12 | --------------------------------------------------------------------------------