tracecontroller 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 976c95bce34722503f83d0204f20af6bf9da43e4460e57a1e43661734e82392d
4
+ data.tar.gz: 8bb332d69f9ebfd511f1ca45069f774d2c9ef93328b109cd6f114829836f6112
5
+ SHA512:
6
+ metadata.gz: 84130ddd4a11c11a9d19d7c1b16f6220a2cacc144b3d69c848428f0bf93a2c7adf7e807bbacc291e7330b81edcfc3ccca131da3f56a3eb8239efa01b3550a70c
7
+ data.tar.gz: 4f90262471650127dacbf3b3b2ad55fb7ea604f8d3c92e8a155f62ea2de999bdc6023a58fe7c3e13454888371580f15b4f9e1e89d3a5d2b20f2f74b8451ced40
@@ -0,0 +1,36 @@
1
+ version: 2.1
2
+
3
+ executors:
4
+ default:
5
+ working_directory: ~/app
6
+ docker:
7
+ - image: circleci/ruby:2.6
8
+
9
+ commands:
10
+ setup_bundle:
11
+ steps:
12
+ - restore_cache:
13
+ key: bundle-{{ checksum "tracecontroller.gemspec" }}
14
+ - run:
15
+ name: install dependencies
16
+ command: |
17
+ bundle install --jobs=4 --retry=3 --path vendor/bundle
18
+ - save_cache:
19
+ key: bundle-{{ checksum "tracecontroller.gemspec" }}
20
+ paths:
21
+ - vendor/bundle
22
+
23
+ jobs:
24
+ test:
25
+ executor: default
26
+ steps:
27
+ - checkout
28
+ - setup_bundle
29
+ - run: bundle exec rspec ./spec
30
+
31
+ workflows:
32
+ version: 2
33
+
34
+ test:
35
+ jobs:
36
+ - test
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tracecontroller.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Akira Kusumoto
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 all
13
+ 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 THE
21
+ SOFTWARE.
@@ -0,0 +1,51 @@
1
+ # tracecontroller
2
+
3
+ A Rake task that helps you find missing callbacks in your Rails app.
4
+
5
+ ## Install
6
+
7
+ Put this line in your Gemfile:
8
+ ```
9
+ gem 'tracecontroller'
10
+ ```
11
+
12
+ Then bundle:
13
+ ```
14
+ % bundle
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ Create a .tracecontroller.yaml or .tracecontroller.yml file in your root directory.
20
+
21
+ ```yaml
22
+ - path: ^/api
23
+ superclass: API::BaseController
24
+ actions:
25
+ - before:
26
+ - require_login_for_api
27
+
28
+ - path: ^/
29
+ actions:
30
+ - before:
31
+ - require_login
32
+ ignore_classes:
33
+ - ^ActionMailbox|^ActiveStorage|^Rails
34
+ - ^API
35
+ ```
36
+
37
+ Just run the following command in your Rails app directory.
38
+
39
+ ```
40
+ % rake tracecontroller
41
+ ```
42
+
43
+ If you want the rake task to fail when errors are found.
44
+
45
+ ```
46
+ % FAIL_ON_ERROR=1 rake tracecontroller
47
+ ```
48
+
49
+ ## Copyright
50
+
51
+ Copyright (c) 2020 Akira Kusumoto. See MIT-LICENSE file for further details.
@@ -0,0 +1,7 @@
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
@@ -0,0 +1,21 @@
1
+ desc 'Prints out missing callbacks'
2
+ task tracecontroller: :environment do
3
+ tracecontroller = Tracecontroller.new(Rails.application)
4
+ tracecontroller.valid?
5
+
6
+ tracecontroller.errors[:callback].each do |error|
7
+ puts "#{error[:controller_name]} Missing callbacks."
8
+
9
+ error[:callbacks].each do |callback|
10
+ puts " #{callback[:kind]}_action: #{callback[:filter]}"
11
+ end
12
+ end
13
+
14
+ tracecontroller.errors[:superclass].each do |name|
15
+ puts "#{name} Missing superclass."
16
+ end
17
+
18
+ if ENV['FAIL_ON_ERROR'] && tracecontroller.valid?
19
+ raise 'Missing callbacks or superclass detected.'
20
+ end
21
+ end
@@ -0,0 +1,110 @@
1
+ class Tracecontroller
2
+ VERSION = Gem.loaded_specs['tracecontroller'].version.to_s
3
+ attr_reader :errors
4
+
5
+ class Railtie < ::Rails::Railtie
6
+ rake_tasks do
7
+ load File.join(File.dirname(__FILE__), "tasks/tracecontroller.rake")
8
+ end
9
+ end
10
+
11
+ def initialize(app)
12
+ @app = app
13
+ end
14
+
15
+ def valid?
16
+ if @errors
17
+ return @errors[:callback].blank? && @errors[:superclass].blank?
18
+ end
19
+
20
+ @app.eager_load!
21
+ @app.reload_routes!
22
+
23
+ routes = collect_routes(Rails.application.routes.routes).reject {|r| r.requirements[:controller].to_s.blank? }
24
+
25
+ @errors = controllers(routes).each.with_object({callback: [], superclass: []}) do |row, errors|
26
+ rules.each do |rule|
27
+ if !rule[:path].match?(row[:path]) || rule[:ignore_classes].any? {|r| r.match? row[:controller_name] }
28
+ next
29
+ end
30
+
31
+ if rule[:superclass].present? && row[:superclass_name] != rule[:superclass]
32
+ errors[:superclass] << row[:controller_name]
33
+ end
34
+
35
+ callbacks = rule[:require_actions] - row[:callbacks]
36
+
37
+ if callbacks.present?
38
+ errors[:callback] << {controller_name: row[:controller_name], callbacks: callbacks}
39
+ end
40
+ end
41
+ end
42
+
43
+ @errors[:callback].blank? && @errors[:superclass].blank?
44
+ end
45
+
46
+ private
47
+
48
+ def config_filename
49
+ %w[.tracecontroller.yaml .tracecontroller.yml].detect { |f| File.exist?(f) }
50
+ end
51
+
52
+ def rules
53
+ @rules ||= (config_filename ? YAML.load_file(config_filename) : []).map do |rule|
54
+ require_actions = (rule["actions"] || []).each.with_object([]) do |h, array|
55
+ h.each do |key, filters|
56
+ filters.each do |filter|
57
+ array << { kind: key.to_sym, filter: filter.to_sym }
58
+ end
59
+ end
60
+ end
61
+
62
+ {
63
+ path: Regexp.new(rule["path"]),
64
+ require_actions: require_actions,
65
+ superclass: rule["superclass"],
66
+ ignore_classes: (rule["ignore_classes"] || []).map {|c| Regexp.new(c) },
67
+ }
68
+ end
69
+ end
70
+
71
+ def controllers(routes)
72
+ routes.sort_by {|r| r.path.spec.to_s }.each.with_object([]) do |route, array|
73
+ controller_name = "#{route.requirements[:controller].camelize}Controller"
74
+
75
+ next if array.any? {|c| c[:controller_name] == controller_name }
76
+
77
+ begin
78
+ controller = ActiveSupport::Dependencies.constantize(controller_name)
79
+ rescue NameError => e
80
+ puts "#{e.message} path:#{route.path.spec}"
81
+ next
82
+ end
83
+
84
+ array << {
85
+ controller_name: controller.name,
86
+ superclass_name: controller.superclass.name,
87
+ callbacks: controller.__callbacks[:process_action].map {|c| { kind: c.kind, filter: c.filter } },
88
+ path: route.path.spec.to_s,
89
+ }
90
+ end
91
+ end
92
+
93
+ def collect_routes(routes)
94
+ routes = routes.each_with_object([]) do |r, tmp_routes|
95
+ next if r.app.is_a?(ActionDispatch::Routing::Mapper::Constraints) && %w[ActionDispatch::Routing::PathRedirect ActionDispatch::Routing::Redirect].include?(r.app.app.class.name)
96
+
97
+ if r.app.is_a?(ActionDispatch::Routing::Mapper::Constraints) && r.app.app.respond_to?(:routes)
98
+ engine_routes = r.app.app.routes
99
+ if engine_routes.is_a?(ActionDispatch::Routing::RouteSet)
100
+ tmp_routes.concat collect_routes(engine_routes.routes)
101
+ end
102
+ else
103
+ tmp_routes << r
104
+ end
105
+ end
106
+
107
+ routes.reject! {|r| r.app.is_a?(ActionDispatch::Routing::Redirect) }
108
+ routes
109
+ end
110
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/all'
4
+ require 'action_controller/railtie'
5
+
6
+ require 'tracecontroller'
7
+
8
+ module DummyApp
9
+ class Application < Rails::Application
10
+ config.root = File.expand_path(__dir__)
11
+ end
12
+ end
13
+
14
+ class ApplicationController < ActionController::Base
15
+ end
16
+
17
+ module Api
18
+ end
19
+
20
+ class Api::BaseController < ApplicationController
21
+ before_action :require_login_for_api
22
+
23
+ def require_login_for_api
24
+ end
25
+ end
@@ -0,0 +1,8 @@
1
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
2
+ require 'pry'
3
+ require 'rails'
4
+ require 'tracecontroller'
5
+ require_relative 'app'
6
+
7
+ # RSpec.configure do |config|
8
+ # end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Tracecontroller do
6
+ it 'has a version number' do
7
+ expect(Tracecontroller::VERSION).not_to be nil
8
+ end
9
+
10
+ after { File.delete '.tracecontroller.yml' if File.exists? '.tracecontroller.yml' }
11
+
12
+ describe '#valid?' do
13
+ it 'missing callback.' do
14
+ DummyApp::Application.routes.draw do
15
+ resources :users
16
+ resources :public_pages
17
+ resources :secrets
18
+ end
19
+
20
+ # OK
21
+ class UsersController < ApplicationController
22
+ before_action :require_login
23
+ end
24
+
25
+ class PublicPagesController < ApplicationController
26
+ end
27
+
28
+ # NG
29
+ class SecretsController < ApplicationController
30
+ end
31
+
32
+ File.open '.tracecontroller.yml', 'w' do |file|
33
+ file.puts '- path: ^/'
34
+ file.puts ' actions:'
35
+ file.puts ' - before:'
36
+ file.puts ' - require_login'
37
+ file.puts ' ignore_classes:'
38
+ file.puts ' - PublicPagesController'
39
+ end
40
+
41
+ trace = Tracecontroller.new(Rails.application)
42
+ expect(trace.valid?).to be_falsey
43
+ expect(trace.errors[:superclass]).to be_blank
44
+ callback_error = {controller_name: 'SecretsController', callbacks: [{kind: :before, filter: :require_login}]}
45
+ expect(trace.errors[:callback]).to eq([callback_error])
46
+ end
47
+
48
+ it 'missing superclass.' do
49
+ DummyApp::Application.routes.draw do
50
+ resources :books
51
+ namespace :api do
52
+ resources :books
53
+ end
54
+ end
55
+
56
+ class BooksController < ActionController::Base
57
+ end
58
+
59
+ class Api::BooksController < ActionController::Base
60
+ end
61
+
62
+ File.open '.tracecontroller.yml', 'w' do |file|
63
+ file.puts '- path: ^/'
64
+ file.puts ' superclass: ApplicationController'
65
+ file.puts ' ignore_classes:'
66
+ file.puts ' - Api::BooksController'
67
+ file.puts '- path: ^/api'
68
+ file.puts ' superclass: Api::BaseController'
69
+ end
70
+
71
+ trace = Tracecontroller.new(Rails.application)
72
+ expect(trace.valid?).to be_falsey
73
+ expect(trace.errors[:superclass]).to eq(["Api::BooksController", "BooksController"])
74
+ expect(trace.errors[:callback]).to be_blank
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,23 @@
1
+ $:.push File.expand_path('lib', __dir__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'tracecontroller'
5
+ s.version = '0.0.1'
6
+ s.platform = Gem::Platform::RUBY
7
+ s.authors = ['Akira Kusumoto']
8
+ s.email = ['akirakusumo10@gmail.com']
9
+ s.homepage = 'https://github.com/bluerabbit/tracecontroller'
10
+ s.summary = 'A Rake task that helps you find the missing callbacks for your Rails app'
11
+ s.description = "This Rake task investigates the application's controller, then tells you missing callbacks"
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
16
+ s.require_paths = ['lib']
17
+
18
+ s.licenses = ['MIT']
19
+
20
+ s.add_dependency 'rails', ['>= 5.0.0']
21
+ s.add_development_dependency 'pry-byebug'
22
+ s.add_development_dependency 'rspec', '~> 3.9'
23
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tracecontroller
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Akira Kusumoto
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-11-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 5.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry-byebug
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.9'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.9'
55
+ description: This Rake task investigates the application's controller, then tells
56
+ you missing callbacks
57
+ email:
58
+ - akirakusumo10@gmail.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".circleci/config.yml"
64
+ - ".gitignore"
65
+ - Gemfile
66
+ - LICENSE
67
+ - README.md
68
+ - Rakefile
69
+ - lib/tasks/tracecontroller.rake
70
+ - lib/tracecontroller.rb
71
+ - spec/app.rb
72
+ - spec/spec_helper.rb
73
+ - spec/tracecontroller_spec.rb
74
+ - tracecontroller.gemspec
75
+ homepage: https://github.com/bluerabbit/tracecontroller
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubygems_version: 3.0.3
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: A Rake task that helps you find the missing callbacks for your Rails app
98
+ test_files:
99
+ - spec/app.rb
100
+ - spec/spec_helper.rb
101
+ - spec/tracecontroller_spec.rb