# frozen_string_literal: true require 'spec_helper' module RailsBestPractices module Reviews describe RemoveUnusedMethodsInModelsReview do let(:runner) do Core::Runner.new( prepares: Prepares::ModelPrepare.new, reviews: described_class.new('except_methods' => ['*#set_cache']) ) end context 'private' do it 'removes unused methods' do content = <<-EOF class Post < ActiveRecord::Base def find; end private def find_by_sql; end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def get Post.new.find end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(1) expect(runner.errors[0].to_s).to eq('app/models/post.rb:4 - remove unused methods (Post#find_by_sql)') end it 'does not remove unused methods with except_methods' do content = <<-EOF class Post < ActiveRecord::Base def set_cache; end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused methods with var_ref' do content = <<-EOF class Post < ActiveRecord::Base def find find_by_sql end private def find_by_sql; end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def get Post.new.find end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused methods with callback' do content = <<-EOF class Post < ActiveRecord::Base after_save :expire_cache private def expire_cache; end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused method with command' do content = <<-EOF class Post < ActiveRecord::Base def fetch get(position: 'first') end private def get(options={}); end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def get Post.new.fetch end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused method with call' do content = <<-EOF class Post < ActiveRecord::Base def conditions self.build_conditions({}) end private def build_conditions(conditions={}); end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def get Post.new.conditions end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused method with message' do content = <<-EOF class Post < ActiveRecord::Base def save transaction true do self.update end end private def transaction(force); end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def create Post.new.save end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused method with validation condition' do content = <<-EOF class Post < ActiveRecord::Base validates_uniqueness_of :login, if: :email_blank? private def email_blank?; end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused method with aasm' do content = <<-EOF class Post < ActiveRecord::Base aasm_state :accepted, enter: [:update_datetime] private def update_datetime; end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused method with initialize' do content = <<-EOF class Post < ActiveRecord::Base private def initialize; end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end end context 'public' do it 'removes unused methods' do content = <<-EOF class Post < ActiveRecord::Base def fetch; end end EOF runner.prepare('app/models/post.rb', content) runner.after_review expect(runner.errors.size).to eq(1) expect(runner.errors[0].to_s).to eq('app/models/post.rb:2 - remove unused methods (Post#fetch)') end it 'does not remove unused methods' do content = <<-EOF class Post < ActiveRecord::Base def fetch; end end EOF runner.prepare('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def show @post.fetch end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused methods for attribute assignment' do content = <<-EOF class Post < ActiveRecord::Base def user=(user); end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused methods for try' do content = <<-EOF class Post < ActiveRecord::Base def find(user_id); end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def find Post.new.try(:find, current_user.id) end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused methods for send' do content = <<-EOF class Post < ActiveRecord::Base def find(user_id); end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def find Post.new.send(:find, current_user.id) end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'removes unused methods for send string_embexpre' do content = <<-EOF class Post < ActiveRecord::Base def find_first; end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def find type = "first" Post.new.send("find_\#{type}") end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(1) end it 'removes unused methods for send variable' do content = <<-EOF class Post < ActiveRecord::Base def first; end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def find type = "first" Post.new.send(type) end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(1) end end context 'protected' do it 'does not remove unused methods' do content = <<-EOF class Post < ActiveRecord::Base protected def test; end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def test Post.new.test end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(1) expect(runner.errors[0].to_s).to eq('app/models/post.rb:3 - remove unused methods (Post#test)') end it 'does not remove unused methods' do post_content = <<-EOF class Post < ActiveRecord::Base protected def test; end end EOF blog_post_content = <<-EOF class BlogPost < Post def play test end end EOF runner.prepare('app/models/post.rb', post_content) runner.prepare('app/models/blog_post.rb', blog_post_content) runner.review('app/models/post.rb', post_content) runner.review('app/models/blog_post.rb', blog_post_content) content = <<-EOF class BlogPostsController < ApplicationController def play BlogPost.new.play end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end end context 'named_scope' do it 'does not remove unused named_scope' do content = <<-EOF class Post < ActiveRecord::Base named_scope :active, conditions: {active: true} end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def index @posts = Post.active end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'removes unused named_scope' do content = <<-EOF class Post < ActiveRecord::Base named_scope :active, conditions: {active: true} end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) runner.after_review expect(runner.errors.size).to eq(1) expect(runner.errors[0].to_s).to eq('app/models/post.rb:2 - remove unused methods (Post#active)') end end context 'scope' do it 'does not remove unused scope' do content = <<-EOF class Post < ActiveRecord::Base scope :active, where(active: true) end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def index @posts = Post.active end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'removes unused named_scope' do content = <<-EOF class Post < ActiveRecord::Base scope :active, where(active: true) end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) runner.after_review expect(runner.errors.size).to eq(1) expect(runner.errors[0].to_s).to eq('app/models/post.rb:2 - remove unused methods (Post#active)') end end context 'alias' do it 'does not remove unused method with alias' do content = <<-EOF class Post < ActiveRecord::Base def old; end alias new old end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def show @post.new end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused method with symbol alias' do content = <<-EOF class Post < ActiveRecord::Base def old; end alias :new :old end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def show @post.new end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused method with alias_method' do content = <<-EOF class Post < ActiveRecord::Base def old; end alias_method :new, :old end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def show @post.new end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused method with alias_method_chain' do content = <<-EOF class Post < ActiveRecord::Base def method_with_feature; end alias_method_chain :method, :feature end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def show @post.method end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end end context 'methods hash' do it 'does not remove unused method with methods hash' do content = <<-EOF class Post < ActiveRecord::Base def to_xml(options = {}) super options.merge(exclude: :visible, methods: [:is_discussion_conversation]) end def is_discussion_conversation; end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end end if RUBY_VERSION.to_f > 3.0 context 'short syntax value' do it 'does not remove unused method' do content = <<-EOF class Post < ActiveRecord::Base def build new(value:) end def value 'value' end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) runner.after_review expect(runner.errors.size).to eq(1) end end end context 'callbacks' do it 'does not remove unused method' do content = <<-EOF class Post < ActiveRecord::Base before_save :init_columns after_destroy :remove_dependencies protected def init_columns; end def remove_dependencies; end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end end context 'validates' do it 'does not remove unused method' do content = <<-EOF class Post < ActiveRecord::Base validate :valid_birth_date protected def valid_birth_date; end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused method for validate_on_create and validate_on_update' do content = <<-EOF class Post < ActiveRecord::Base validate_on_create :valid_email validate_on_update :valid_birth_date protected def valid_email; end def valid_birth_date; end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused methods for to_param' do content = <<-EOF class Post < ActiveRecord::Base def to_param id end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end end context 'helper method' do it 'does not remove unused method for coommand_call collection_select' do content = <<-EOF class Category < ActiveRecord::Base def indented_name; end end EOF runner.prepare('app/models/category.rb', content) runner.review('app/models/category.rb', content) content = <<-EOF <%= f.collection_select :parent_id, Category.all_hierarchic(except: @category), :id, :indented_name, {include_blank: true} %> EOF runner.review('app/views/categories/_form.html.erb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused method for command collection_select' do content = <<-EOF class Category < ActiveRecord::Base def indented_name; end end EOF runner.prepare('app/models/category.rb', content) runner.review('app/models/category.rb', content) content = <<-EOF <%= collection_select :category, :parent_id, Category.all_hierarchic(except: @category), :id, :indented_name, {include_blank: true} %> EOF runner.review('app/views/categories/_form.html.erb', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not remove unused method for options_from_collection_for_select' do content = <<-EOF class Category < ActiveRecord::Base def indented_name; end end EOF runner.prepare('app/models/category.rb', content) runner.review('app/models/category.rb', content) content = <<-EOF <%= select_tag 'category', options_from_collection_for_select(Category.all_hierachic(except: @category), :id, :indented_name) %> EOF runner.review('app/views/categories/_form.html.erb', content) runner.after_review expect(runner.errors.size).to eq(0) end end it 'does not remove unused methods for rabl view' do content = <<-EOF class User def first_name; end def last_name; end end EOF runner.prepare('app/models/user.rb', content) runner.review('app/models/user.rb', content) content = <<-EOF node :full_name do |u| u.first_name + " " + u.last_name end EOF runner.review('app/views/users/show.json.rabl', content) runner.after_review expect(runner.errors.size).to eq(0) end it 'does not skip :call as call message' do content = <<-EOF module DateRange RANGES = lambda { last_month = { 'range' => lambda { [date_from_time.(31.days.ago), date_from_time.(Time.now)] }, 'value' => 'last_month', 'label' => 'Last month'} }[] end EOF runner.prepare('app/mixins/date_range.rb', content) runner.review('app/mixins/date_range.rb', content) end it 'does not check ignored files' do runner = Core::Runner.new( prepares: Prepares::ModelPrepare.new, reviews: described_class.new(except_methods: [], ignored_files: /post/) ) content = <<-EOF class Post < ActiveRecord::Base def find; end private def find_by_sql; end end EOF runner.prepare('app/models/post.rb', content) runner.review('app/models/post.rb', content) content = <<-EOF class PostsController < ApplicationController def get Post.new.find end end EOF runner.review('app/controllers/posts_controller.rb', content) runner.after_review expect(runner.errors.size).to eq(0) end end end end