├── .rspec ├── Gemfile ├── okuribito_logo.png ├── lib ├── okuribito │ ├── version.rb │ ├── okuribito_patch.rb │ ├── request.rb │ ├── patch_module.rb │ └── patcher.rb └── okuribito.rb ├── .gitignore ├── Rakefile ├── spec ├── spec_helper.rb ├── support │ ├── good_test_config.yml │ ├── bad_test_config.yml │ └── test_target.rb ├── patcher_spec.rb └── okuribito_patch_spec.rb ├── circle.yml ├── .rubocop.yml ├── CHANGELOG.md ├── LICENSE.txt ├── okuribito.gemspec └── README.md /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gemspec 3 | -------------------------------------------------------------------------------- /okuribito_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shakemurasan/okuribito/HEAD/okuribito_logo.png -------------------------------------------------------------------------------- /lib/okuribito/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module Okuribito 3 | VERSION = "0.3.0".freeze 4 | end 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task default: :spec 7 | -------------------------------------------------------------------------------- /lib/okuribito.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "okuribito/version" 3 | require "okuribito/okuribito_patch" 4 | 5 | module Okuribito 6 | end 7 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "simplecov" 3 | 4 | SimpleCov.start do 5 | add_filter "/vendor/" 6 | add_filter "/spec/" 7 | end 8 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | timezone: Asia/Tokyo 3 | ruby: 4 | version: 5 | 2.2.2 6 | test: 7 | override: 8 | - bundle exec rspec 9 | - bundle exec codeclimate-test-reporter -------------------------------------------------------------------------------- /lib/okuribito/okuribito_patch.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "okuribito/request" 3 | 4 | # TODO: Remain for backward compatibility, but actually should delete this class. 5 | module Okuribito 6 | class OkuribitoPatch < Request 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/support/good_test_config.yml: -------------------------------------------------------------------------------- 1 | TestTarget: 2 | - ".deprecated_self_method" 3 | - "#deprecated_method" 4 | - "#deprecated_method?" 5 | - "#deprecated_method!" 6 | TestModule::TestTarget: 7 | - "#deprecated_method" 8 | TestModule::NestedTestModule::TestTarget: 9 | - "#deprecated_method" 10 | -------------------------------------------------------------------------------- /spec/support/bad_test_config.yml: -------------------------------------------------------------------------------- 1 | TestTarget: 2 | - ".deprecated_self_method" 3 | - "#deprecated_method" 4 | - "#deprecated_method?" 5 | - "#deprecated_method!" 6 | UnknownTarget: 7 | - ".unknown_method" 8 | TestModule::TestTarget: 9 | - "#deprecated_method" 10 | TestModule::NestedTestModule::TestTarget: 11 | - "#deprecated_method" 12 | -------------------------------------------------------------------------------- /spec/support/test_target.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class TestTarget 3 | def self.deprecated_self_method; end 4 | 5 | def deprecated_method; end 6 | 7 | def deprecated_method?; end 8 | 9 | def deprecated_method!; end 10 | end 11 | 12 | module TestModule 13 | module NestedTestModule 14 | class TestTarget 15 | def deprecated_method; end 16 | end 17 | end 18 | 19 | class TestTarget 20 | def deprecated_method; end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Exclude: 3 | - 'okuribito.gemspec' 4 | - 'spec/spec_helper.rb' 5 | 6 | Documentation: 7 | Enabled: false 8 | 9 | LineLength: 10 | Max: 100 11 | Exclude: 12 | - "spec/**/*" 13 | 14 | Style/StringLiterals: 15 | EnforcedStyle: double_quotes 16 | SupportedStyles: 17 | - double_quotes 18 | 19 | Metrics/CyclomaticComplexity: 20 | Max: 10 21 | 22 | Metrics/PerceivedComplexity: 23 | Max: 10 24 | 25 | Metrics/MethodLength: 26 | Max: 40 27 | 28 | Metrics/AbcSize: 29 | Max: 40 30 | 31 | Metrics/BlockLength: 32 | Exclude: 33 | - 'spec/**/*' 34 | -------------------------------------------------------------------------------- /lib/okuribito/request.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "okuribito/patcher" 3 | require "yaml" 4 | 5 | module Okuribito 6 | class Request 7 | def initialize(opt = {}, &callback) 8 | @patcher = Patcher.new(opt, callback) 9 | end 10 | 11 | def apply(yaml_path) 12 | yaml = YAML.load_file(yaml_path) 13 | yaml.each do |class_name, observe_methods| 14 | @patcher.patch_okuribito(class_name, observe_methods) 15 | end 16 | end 17 | 18 | def apply_one(full_method_name) 19 | class_name, symbol, method_name = full_method_name.split(/(\.|#)/) 20 | @patcher.patch_okuribito(class_name, [symbol + method_name]) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/okuribito/patch_module.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module Okuribito 3 | module SimplePatchModule 4 | private 5 | 6 | def define_patch(method_name, _patch, _id, _opt = {}) 7 | define_method(method_name) do |*args| 8 | yield(to_s, caller) if block_given? 9 | super(*args) 10 | end 11 | end 12 | end 13 | 14 | module FunctionalPatchModule 15 | private 16 | 17 | def define_patch(method_name, patch, id, opt = {}) 18 | sn = method_name.to_s.gsub(/\?/, "__q").gsub(/!/, "__e").gsub(/=/, "__eq") 19 | patch.instance_variable_set("@#{sn}_#{id}_called", false) 20 | define_method(method_name) do |*args| 21 | if block_given? && !patch.instance_variable_get("@#{sn}_#{id}_called") 22 | yield(to_s, caller) 23 | patch.instance_variable_set("@#{sn}_#{id}_called", true) if opt[:once_detect] 24 | end 25 | super(*args) 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | ## [0.3.0](https://github.com/muramurasan/okuribito_rails/releases/tag/v0.3.0) (July 28, 2018) 3 | * FIX: 4 | * Problems that cause errors when monitoring nonexistent classes. #39 5 | * Refactoring: 6 | * Extract class and methods. 7 | * Fix rspec. 8 | 9 | ## [0.2.3](https://github.com/muramurasan/okuribito_rails/releases/tag/v0.2.3) (February 5, 2017) 10 | * Feature: Monitor a single method with a string specification. 11 | * Feature: Make arguments referable when executing code. 12 | 13 | ## [0.2.2](https://github.com/muramurasan/okuribito_rails/releases/tag/v0.2.2) (January 25, 2017) 14 | * Fix: Stop confirming the existence of a namespace / class. #35 15 | 16 | ## [0.2.1](https://github.com/muramurasan/okuribito_rails/releases/tag/v0.2.1) (January 25, 2017) 17 | * Fix: Assignment operator causes error #34 18 | 19 | ## [0.2.0](https://github.com/muramurasan/okuribito_rails/releases/tag/v0.2.0) (January 8, 2017) 20 | * Correspond to namespace 21 | 22 | ## [0.1.7](https://github.com/muramurasan/okuribito_rails/releases/tag/v0.1.7) (December 7, 2016) 23 | * The first stable release 24 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 yasuhiro_matsumura 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 | -------------------------------------------------------------------------------- /spec/patcher_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | require "okuribito" 4 | 5 | describe Okuribito::Patcher do 6 | let(:option) { {} } 7 | let(:callback) { proc { |method_name, _obj_name, _caller_info, class_name, method_symbol| output.puts "#{class_name}#{method_symbol}#{method_name}" } } 8 | let(:patcher) { Okuribito::Patcher.new(option, callback) } 9 | 10 | describe "#patch_okuribito" do 11 | context "when target undefined class" do 12 | subject { patcher.patch_okuribito("UndefinedTestClass", ["#deprecated_method"]) } 13 | 14 | it do 15 | expect(patcher).to receive(:process_undefined_class) 16 | expect { subject }.not_to raise_error 17 | end 18 | end 19 | 20 | context "when target undefined class method" do 21 | subject { patcher.patch_okuribito("TestTarget", [".undefined_method"]) } 22 | 23 | it { expect { subject }.not_to raise_error } 24 | end 25 | 26 | context "when target undefined instance method" do 27 | subject { patcher.patch_okuribito("TestTarget", ["#undefined_method"]) } 28 | 29 | it { expect { subject }.not_to raise_error } 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /okuribito.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'okuribito/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "okuribito" 8 | spec.version = Okuribito::VERSION 9 | spec.authors = ["muramurasan"] 10 | spec.email = ["ym.works1985@gmail.com"] 11 | 12 | spec.summary = "Okuribito monitors the method call, and exec specified code." 13 | spec.description = "Okuribito monitors the method call, and exec specified code." 14 | spec.homepage = "https://github.com/muramurasan/okuribito" 15 | spec.license = "MIT" 16 | 17 | spec.required_ruby_version = '>= 2.0.0' 18 | 19 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 20 | spec.bindir = "exe" 21 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 22 | spec.require_paths = ["lib"] 23 | 24 | spec.add_dependency "activesupport", ">= 4.0" 25 | 26 | spec.add_development_dependency "rspec", "~> 3.0" 27 | spec.add_development_dependency "rubocop" 28 | spec.add_development_dependency "simplecov" 29 | spec.add_development_dependency "codeclimate-test-reporter", "~> 1.0.0" 30 | end 31 | -------------------------------------------------------------------------------- /lib/okuribito/patcher.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "okuribito/patch_module" 3 | require "active_support" 4 | require "active_support/core_ext" 5 | 6 | module Okuribito 7 | class Patcher 8 | CLASS_METHOD_SYMBOL = ".".freeze 9 | INSTANCE_METHOD_SYMBOL = "#".freeze 10 | PATTERN = /\A(?[#{CLASS_METHOD_SYMBOL}#{INSTANCE_METHOD_SYMBOL}])(?.+)\z/ 11 | 12 | def initialize(opt, callback) 13 | @opt = opt 14 | @callback = callback 15 | end 16 | 17 | def patch_okuribito(full_class_name, observe_methods) 18 | opt = @opt 19 | callback = @callback 20 | klass = full_class_name.safe_constantize 21 | unless klass 22 | process_undefined_class(full_class_name) 23 | return 24 | end 25 | uniq_constant = full_class_name.gsub(/::/, "Sp") 26 | i_method_patch = patch_module("#{uniq_constant}InstancePatch", opt) 27 | c_method_patch = patch_module("#{uniq_constant}ClassPatch", opt) 28 | i_method_patched = 0 29 | c_method_patched = 0 30 | 31 | klass.class_eval do 32 | observe_methods.each do |observe_method| 33 | next unless (md = PATTERN.match(observe_method)) 34 | symbol = md[:symbol] 35 | method_name = md[:method_name].to_sym 36 | 37 | case symbol 38 | when INSTANCE_METHOD_SYMBOL 39 | next unless klass.instance_methods.include?(method_name) 40 | i_method_patch.module_eval do 41 | define_patch(method_name, i_method_patch, "i", opt) do |obj_name, caller_info| 42 | callback.call(method_name, obj_name, caller_info, full_class_name, symbol) 43 | end 44 | end 45 | i_method_patched += 1 46 | when CLASS_METHOD_SYMBOL 47 | next unless klass.respond_to?(method_name) 48 | c_method_patch.module_eval do 49 | define_patch(method_name, c_method_patch, "c", opt) do |obj_name, caller_info| 50 | callback.call(method_name, obj_name, caller_info, full_class_name, symbol) 51 | end 52 | end 53 | c_method_patched += 1 54 | end 55 | end 56 | prepend i_method_patch if i_method_patched > 0 57 | singleton_class.send(:prepend, c_method_patch) if c_method_patched > 0 58 | end 59 | end 60 | 61 | private 62 | 63 | def patch_module(patch_name, opt) 64 | if opt.present? 65 | if FunctionalPatchModule.const_defined?(patch_name) 66 | Module.new.extend(FunctionalPatchModule) 67 | else 68 | FunctionalPatchModule.const_set(patch_name, Module.new.extend(FunctionalPatchModule)) 69 | end 70 | else 71 | Module.new.extend(SimplePatchModule) 72 | end 73 | end 74 | 75 | def process_undefined_class(_full_class_name) 76 | # do nothing.... 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CircleCI](https://circleci.com/gh/muramurasan/okuribito/tree/master.svg?style=svg)](https://circleci.com/gh/muramurasan/okuribito/tree/master) 2 | [![Code Climate](https://codeclimate.com/github/muramurasan/okuribito.png)](https://codeclimate.com/github/muramurasan/okuribito) 3 | [![Test Coverage](https://codeclimate.com/github/muramurasan/okuribito/badges/coverage.svg)](https://codeclimate.com/github/muramurasan/okuribito/coverage) 4 | 5 | # Okuribito 6 | 7 | https://rubygems.org/gems/okuribito 8 | 9 | ![okuribito](okuribito_logo.png) 10 | 11 | Okuribito is a gem to judge whether methods should be sent to the heaven :innocent:. 12 | 13 | Okuribito monitors the method call with YAML, and exec specified code. 14 | 15 | In other words, it can be used in order to extract the uncalled method. 16 | 17 | Okuribito was named after a japanese movie. 18 | 19 | ## Installation 20 | 21 | Add this line to your application's Gemfile: 22 | 23 | ```ruby 24 | gem 'okuribito' 25 | ``` 26 | 27 | And then execute: 28 | 29 | $ bundle 30 | 31 | Or install it yourself as: 32 | 33 | $ gem install okuribito 34 | 35 | ## Usage 36 | 37 | Add `config/okuribito.yml` and edit it. 38 | 39 | ```yml 40 | User: 41 | - '#feed' 42 | Micropost: 43 | - '.from_users_followed_by' 44 | Admin::Manage: 45 | - '.add_user' 46 | ``` 47 | 48 | By writing the following code to start the monitoring of the method. 49 | 50 | ```ruby 51 | okuribito = Okuribito::Request.new do |method_name, obj_name, caller_info| 52 | # do something as you like! 53 | end 54 | okuribito.apply("config/okuribito.yml") 55 | ``` 56 | 57 | You can also give the option. 58 | 59 | `once_detect`: When it detects a method call, and run only once the code that has been set. 60 | 61 | ```ruby 62 | okuribito = Okuribito::Request.new(once_detect: true) do |method_name, obj_name, caller_info| 63 | # do something as you like! 64 | end 65 | okuribito.apply("config/okuribito.yml") 66 | ``` 67 | 68 | You can also monitor a single method with a string specification. 69 | 70 | ```ruby 71 | okuribito = Okuribito::Request.new do |method_name, obj_name, caller_info| 72 | # do something as you like! 73 | end 74 | okuribito.apply_one("TestTarget#deprecated_method") 75 | ``` 76 | 77 | You can use the following parameters when executing arbitrary code. 78 | 79 | * method_name 80 | * obj_name 81 | * caller_info (backtrace) 82 | * class_name 83 | * symbol (`.` or `#`) 84 | * args 85 | 86 | ```ruby 87 | okuribito = Okuribito::Request.new do |method_name, obj_name, caller_info, class_name, symbol, args| 88 | # do something as you like! 89 | end 90 | okuribito.apply_one("TestTarget#deprecated_method_with_args") 91 | ``` 92 | 93 | ### ex: Ruby On Rails 94 | 95 | Edit `application.rb` 96 | 97 | ```ruby 98 | class OkuribitoSetting < Rails::Railtie 99 | config.after_initialize do 100 | okuribito = Okuribito::Request.new do |method_name, obj_name, caller_info| 101 | # do something as you like! 102 | end 103 | okuribito.apply("config/okuribito.yml") 104 | end 105 | end 106 | ``` 107 | 108 | ## The smallest example 109 | 110 | ```ruby 111 | require "bundler/setup" 112 | require "okuribito" 113 | 114 | class TestTarget 115 | def self.deprecated_self_method 116 | end 117 | 118 | def deprecated_method 119 | end 120 | end 121 | 122 | okuribito = Okuribito::Request.new do |method_name, obj_name, caller_info| 123 | puts "#{obj_name} #{method_name} #{caller_info[0]}" 124 | end 125 | okuribito.apply("config/okuribito.yml") 126 | 127 | TestTarget.deprecated_self_method 128 | TestTarget.new.deprecated_method 129 | ``` 130 | 131 | Setting file: 132 | 133 | ```okuribito.yml 134 | TestTarget: 135 | - ".deprecated_self_method" 136 | - "#deprecated_method" 137 | 138 | ``` 139 | 140 | Output: 141 | 142 | ```output 143 | TestTarget deprecated_self_method example.rb:17:in `
' 144 | # deprecated_method example.rb:18:in `
' 145 | ``` 146 | 147 | ## Callback examples 148 | 149 | ### Full stacktrace 150 | 151 | ```ruby 152 | okuribito = Okuribito::Request.new do |method_name, obj_name, caller_info| 153 | puts "#############################################################" 154 | puts "#{obj_name} #{method_name} #{caller_info[0]}" 155 | puts "#############################################################" 156 | puts caller_info 157 | end 158 | okuribito.apply("config/okuribito.yml") 159 | ``` 160 | 161 | ### Other ideas 162 | - Send to Fluentd, TreasureData, Slack... 163 | 164 | ## Compatibility 165 | - `Okuribito::OkuribitoPatch` has backward compatibility, but it is old class! 166 | - Later version 0.3.0, you should use `Okuribito::Request` 167 | 168 | 169 | ## License 170 | 171 | The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). 172 | Copyright 2016 Yasuhiro Matsumura. 173 | -------------------------------------------------------------------------------- /spec/okuribito_patch_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | require "support/test_target" 4 | require "okuribito" 5 | 6 | describe Okuribito::Request do 7 | let(:setting_path) { "spec/support/good_test_config.yml" } 8 | let(:dummy_caller) { ["dummy_caller"] } 9 | let(:output) { StringIO.new } 10 | let(:option) { {} } 11 | let(:request) { Okuribito::Request.new(option, &callback) } 12 | let(:callback) { proc { |method_name, obj_name, caller_info, _class_name, _method_symbol| output.puts "#{obj_name} #{method_name} #{caller_info[0]}" } } 13 | 14 | describe "simple version" do 15 | before do 16 | allow_any_instance_of(Kernel).to receive(:caller).and_return(dummy_caller) 17 | end 18 | 19 | describe "#apply" do 20 | before { request.apply(setting_path) } 21 | 22 | subject { output.string.chomp } 23 | 24 | context "when target class method called" do 25 | before { TestTarget.deprecated_self_method } 26 | 27 | it { is_expected.to eq "TestTarget deprecated_self_method #{dummy_caller[0]}" } 28 | end 29 | 30 | context "when target class method called twice" do 31 | before { 2.times { TestTarget.deprecated_self_method } } 32 | 33 | context "(no option)" do 34 | it { is_expected.to eq "TestTarget deprecated_self_method #{dummy_caller[0]}\nTestTarget deprecated_self_method #{dummy_caller[0]}" } 35 | end 36 | 37 | context "(option: once detect)" do 38 | let(:option) { { once_detect: true } } 39 | 40 | it { is_expected.to eq "TestTarget deprecated_self_method #{dummy_caller[0]}" } 41 | end 42 | end 43 | 44 | context "when target instance method called" do 45 | before { TestTarget.new.deprecated_method } 46 | 47 | it { is_expected.to match "# deprecated_method #{dummy_caller[0]}" } 48 | end 49 | 50 | context "when target instance method called (methods ending in ?)" do 51 | before { TestTarget.new.deprecated_method? } 52 | 53 | it { is_expected.to match "# deprecated_method\\? #{dummy_caller[0]}" } 54 | end 55 | 56 | context "when target instance method called (methods ending in !)" do 57 | before { TestTarget.new.deprecated_method! } 58 | 59 | it { is_expected.to match "# deprecated_method! #{dummy_caller[0]}" } 60 | end 61 | 62 | context "when target instance method called twice" do 63 | let(:test_target) { TestTarget.new } 64 | 65 | before do 66 | 2.times { test_target.deprecated_method } 67 | TestTarget.new.deprecated_method 68 | end 69 | 70 | context "(no option)" do 71 | it { is_expected.to match "# deprecated_method #{dummy_caller[0]}\n" } 72 | end 73 | 74 | context "(option: once detect)" do 75 | let(:option) { { once_detect: true } } 76 | 77 | it { is_expected.to match "# deprecated_method #{dummy_caller[0]}" } 78 | it { is_expected.not_to match "# deprecated_method #{dummy_caller[0]}\n" } 79 | end 80 | end 81 | 82 | context "when target instance method called (class under module)" do 83 | let(:test_target) { TestModule::TestTarget.new } 84 | 85 | before { test_target.deprecated_method } 86 | 87 | it { is_expected.to match "# deprecated_method #{dummy_caller[0]}" } 88 | end 89 | 90 | context "when target instance method called (class under nested module)" do 91 | let(:test_target) { TestModule::NestedTestModule::TestTarget.new } 92 | 93 | before { test_target.deprecated_method } 94 | 95 | it { is_expected.to match "# deprecated_method #{dummy_caller[0]}" } 96 | end 97 | end 98 | 99 | describe "#apply_one" do 100 | subject { output.string.chomp } 101 | 102 | context "when target class method called" do 103 | before do 104 | request.apply_one("TestTarget.deprecated_self_method") 105 | TestTarget.deprecated_self_method 106 | end 107 | 108 | it { is_expected.to eq "TestTarget deprecated_self_method #{dummy_caller[0]}" } 109 | end 110 | 111 | context "when target instance method called" do 112 | before do 113 | request.apply_one("TestTarget#deprecated_method") 114 | TestTarget.new.deprecated_method 115 | end 116 | 117 | it { is_expected.to match "# deprecated_method #{dummy_caller[0]}" } 118 | end 119 | end 120 | end 121 | 122 | describe "functional version" do 123 | let(:callback) { proc { |method_name, _obj_name, _caller_info, class_name, method_symbol| output.puts "#{class_name}#{method_symbol}#{method_name}" } } 124 | 125 | describe "#apply" do 126 | before { request.apply(setting_path) } 127 | 128 | subject { output.string.chomp } 129 | 130 | context "when target class method called" do 131 | before { TestTarget.deprecated_self_method } 132 | 133 | it { is_expected.to eq "TestTarget.deprecated_self_method" } 134 | end 135 | 136 | context "when target instance method called" do 137 | context "(normal name)" do 138 | before { TestTarget.new.deprecated_method } 139 | 140 | it { is_expected.to eq "TestTarget#deprecated_method" } 141 | end 142 | 143 | context "(methods ending in ?)" do 144 | before { TestTarget.new.deprecated_method? } 145 | 146 | it { is_expected.to eq "TestTarget#deprecated_method?" } 147 | end 148 | 149 | context "(methods ending in !)" do 150 | before { TestTarget.new.deprecated_method! } 151 | 152 | it { is_expected.to eq "TestTarget#deprecated_method!" } 153 | end 154 | end 155 | 156 | context "when target instance method called (class under module)" do 157 | before { TestModule::TestTarget.new.deprecated_method } 158 | 159 | it { is_expected.to eq "TestModule::TestTarget#deprecated_method" } 160 | end 161 | 162 | context "when target instance method called (class under nested module)" do 163 | before { TestModule::NestedTestModule::TestTarget.new.deprecated_method } 164 | 165 | it { is_expected.to eq "TestModule::NestedTestModule::TestTarget#deprecated_method" } 166 | end 167 | end 168 | 169 | describe "#apply_one" do 170 | subject { output.string.chomp } 171 | 172 | context "when target class method called" do 173 | before do 174 | request.apply_one("TestTarget.deprecated_self_method") 175 | TestTarget.deprecated_self_method 176 | end 177 | 178 | it { is_expected.to eq "TestTarget.deprecated_self_method" } 179 | end 180 | 181 | context "when target instance method called" do 182 | context "(normal name)" do 183 | before do 184 | request.apply_one("TestTarget#deprecated_method") 185 | TestTarget.new.deprecated_method 186 | end 187 | 188 | it { is_expected.to eq "TestTarget#deprecated_method" } 189 | end 190 | end 191 | end 192 | end 193 | end 194 | --------------------------------------------------------------------------------