tracecontroller 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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