did_you_mean 0.9.6-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +27 -0
  3. data/.travis.yml +35 -0
  4. data/Appraisals +23 -0
  5. data/Gemfile +18 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +110 -0
  8. data/Rakefile +61 -0
  9. data/did_you_mean.gemspec +35 -0
  10. data/ext/did_you_mean/extconf.rb +3 -0
  11. data/ext/did_you_mean/method_missing.c +20 -0
  12. data/ext/did_you_mean/org/yukinishijima/ReceiverCapturer.java +57 -0
  13. data/gemfiles/activerecord_32.gemfile +21 -0
  14. data/gemfiles/activerecord_40.gemfile +21 -0
  15. data/gemfiles/activerecord_41.gemfile +21 -0
  16. data/gemfiles/activerecord_42.gemfile +21 -0
  17. data/gemfiles/activerecord_edge.gemfile +25 -0
  18. data/lib/did_you_mean.rb +16 -0
  19. data/lib/did_you_mean/core_ext/name_error.rb +28 -0
  20. data/lib/did_you_mean/finders.rb +31 -0
  21. data/lib/did_you_mean/finders/name_error_finders.rb +24 -0
  22. data/lib/did_you_mean/finders/name_error_finders/similar_class_finder.rb +50 -0
  23. data/lib/did_you_mean/finders/name_error_finders/similar_name_finder.rb +61 -0
  24. data/lib/did_you_mean/finders/similar_attribute_finder.rb +26 -0
  25. data/lib/did_you_mean/finders/similar_method_finder.rb +45 -0
  26. data/lib/did_you_mean/levenshtein.rb +49 -0
  27. data/lib/did_you_mean/receiver_capturer.jar +0 -0
  28. data/lib/did_you_mean/test_helper.rb +7 -0
  29. data/lib/did_you_mean/version.rb +3 -0
  30. data/lib/did_you_mean/word_collection.rb +27 -0
  31. data/test/all_test.rb +17 -0
  32. data/test/name_error_extension_test.rb +65 -0
  33. data/test/no_method_error_extension_test.rb +14 -0
  34. data/test/null_finder_test.rb +19 -0
  35. data/test/similar_attribute_finder_test.rb +17 -0
  36. data/test/similar_class_finder_test.rb +85 -0
  37. data/test/similar_method_finder_test.rb +60 -0
  38. data/test/similar_name_finder_test.rb +62 -0
  39. data/test/test_helper.rb +32 -0
  40. metadata +165 -0
@@ -0,0 +1,21 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.0.0"
6
+
7
+ platforms :ruby do
8
+ gem "sqlite3"
9
+ end
10
+
11
+ platforms :jruby do
12
+ gem "activerecord-jdbcsqlite3-adapter"
13
+ end
14
+
15
+ platforms :rbx do
16
+ gem "rubysl", "~> 2.0"
17
+ gem "racc"
18
+ gem "rubinius-developer_tools"
19
+ end
20
+
21
+ gemspec :path => "../"
@@ -0,0 +1,21 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.1.0"
6
+
7
+ platforms :ruby do
8
+ gem "sqlite3"
9
+ end
10
+
11
+ platforms :jruby do
12
+ gem "activerecord-jdbcsqlite3-adapter"
13
+ end
14
+
15
+ platforms :rbx do
16
+ gem "rubysl", "~> 2.0"
17
+ gem "racc"
18
+ gem "rubinius-developer_tools"
19
+ end
20
+
21
+ gemspec :path => "../"
@@ -0,0 +1,21 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.2.0"
6
+
7
+ platforms :ruby do
8
+ gem "sqlite3"
9
+ end
10
+
11
+ platforms :jruby do
12
+ gem "activerecord-jdbcsqlite3-adapter"
13
+ end
14
+
15
+ platforms :rbx do
16
+ gem "rubysl", "~> 2.0"
17
+ gem "racc"
18
+ gem "rubinius-developer_tools"
19
+ end
20
+
21
+ gemspec :path => "../"
@@ -0,0 +1,25 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ git "git://github.com/rails/rails.git" do
6
+ gem "activerecord", :require => "activerecord"
7
+ end
8
+
9
+ gem "arel", :github => "rails/arel"
10
+
11
+ platforms :ruby do
12
+ gem "sqlite3"
13
+ end
14
+
15
+ platforms :jruby do
16
+ gem "activerecord-jdbcsqlite3-adapter"
17
+ end
18
+
19
+ platforms :rbx do
20
+ gem "rubysl", "~> 2.0"
21
+ gem "racc"
22
+ gem "rubinius-developer_tools"
23
+ end
24
+
25
+ gemspec :path => "../"
@@ -0,0 +1,16 @@
1
+ require "interception"
2
+
3
+ require "did_you_mean/version"
4
+ require "did_you_mean/core_ext/name_error"
5
+ require "did_you_mean/finders"
6
+
7
+ module DidYouMean
8
+ Interception.listen(->(exception, binding) {
9
+ # On IRB/pry console, this event is called twice. In the second event,
10
+ # we get IRB/pry binding. So it shouldn't override @frame_binding if
11
+ # it's already defined.
12
+ if exception.is_a?(NameError) && !exception.instance_variable_defined?(:@frame_binding)
13
+ exception.instance_variable_set(:@frame_binding, binding)
14
+ end
15
+ })
16
+ end
@@ -0,0 +1,28 @@
1
+ class NameError
2
+ attr_reader :frame_binding
3
+
4
+ IGNORED_CALLERS = [
5
+ /( |`)missing_name'/,
6
+ /( |`)safe_constantize'/
7
+ ].freeze
8
+ private_constant :IGNORED_CALLERS
9
+
10
+ def to_s_with_did_you_mean
11
+ msg = original_message
12
+ msg << did_you_mean?.to_s if IGNORED_CALLERS.all? {|ignored| caller.first(8).grep(ignored).empty? }
13
+ msg
14
+ rescue
15
+ original_message
16
+ end
17
+
18
+ alias original_message to_s
19
+ alias to_s to_s_with_did_you_mean
20
+
21
+ def did_you_mean?
22
+ finder.did_you_mean?
23
+ end
24
+
25
+ def finder
26
+ @finder ||= DidYouMean.finders[self.class.to_s].new(self)
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ require "did_you_mean/word_collection"
2
+
3
+ module DidYouMean
4
+ module BaseFinder
5
+ def did_you_mean?
6
+ return if similar_words.empty?
7
+
8
+ output = "\n\n"
9
+ output << " Did you mean? #{format(similar_words.first)}\n"
10
+ output << similar_words.drop(1).map{|word| "#{' ' * 18}#{format(word)}\n" }.join
11
+ output << " " # for pry
12
+ end
13
+
14
+ def similar_words
15
+ @similar_words ||= WordCollection.new(words).similar_to(target_word)
16
+ end
17
+ end
18
+
19
+ class NullFinder
20
+ def initialize(*); end
21
+ def did_you_mean?; end
22
+ end
23
+
24
+ def self.finders
25
+ @@finders ||= Hash.new(NullFinder)
26
+ end
27
+ end
28
+
29
+ require 'did_you_mean/finders/name_error_finders'
30
+ require 'did_you_mean/finders/similar_attribute_finder'
31
+ require 'did_you_mean/finders/similar_method_finder'
@@ -0,0 +1,24 @@
1
+ module DidYouMean
2
+ module NameErrorFinders
3
+ def self.included(*)
4
+ raise "Do not include this module since it overrides Class.new method."
5
+ end
6
+
7
+ def self.new(exception)
8
+ klass = if /uninitialized constant/ =~ exception.original_message
9
+ SimilarClassFinder
10
+ elsif /undefined local variable or method/ =~ exception.original_message
11
+ SimilarNameFinder
12
+ else
13
+ NullFinder
14
+ end
15
+
16
+ klass.new(exception)
17
+ end
18
+ end
19
+
20
+ finders["NameError"] = NameErrorFinders
21
+ end
22
+
23
+ require 'did_you_mean/finders/name_error_finders/similar_name_finder'
24
+ require 'did_you_mean/finders/name_error_finders/similar_class_finder'
@@ -0,0 +1,50 @@
1
+ module DidYouMean
2
+ class SimilarClassFinder
3
+ include BaseFinder
4
+ attr_reader :class_name, :original_message
5
+
6
+ def initialize(exception)
7
+ @class_name, @original_message = exception.name, exception.original_message
8
+ end
9
+
10
+ def words
11
+ scopes.flat_map do |scope|
12
+ scope.constants.map {|name| ConstantName.new(name, scope) }
13
+ end
14
+ end
15
+
16
+ def name_from_message
17
+ class_name || /([A-Z]\w*$)/.match(original_message)[0]
18
+ end
19
+ alias target_word name_from_message
20
+
21
+ def similar_words
22
+ super.map(&:full_name)
23
+ end
24
+
25
+ def scopes
26
+ @scopes ||= scope_base.size.times.map do |count|
27
+ eval(scope_base[0..(- count)].join("::"))
28
+ end.reverse << Object
29
+ end
30
+
31
+ private
32
+
33
+ def scope_base
34
+ @scope_base ||= (/(([A-Z]\w*::)*)([A-Z]\w*)$/ =~ original_message ? $1 : "").split("::")
35
+ end
36
+
37
+ class ConstantName < String
38
+ attr_reader :scope
39
+
40
+ def initialize(str, scope)
41
+ super(str.to_s)
42
+ @scope = scope.to_s
43
+ end
44
+
45
+ def full_name
46
+ scope == "Object" ? to_s : "#{scope}::#{to_s}"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,61 @@
1
+ module DidYouMean
2
+ class SimilarNameFinder
3
+ include BaseFinder
4
+ attr_reader :name, :_methods, :_local_variables, :_instance_variables
5
+
6
+ def initialize(exception)
7
+ @name = exception.name
8
+ @_methods = exception.frame_binding.eval("methods")
9
+ @_local_variables = exception.frame_binding.eval("local_variables")
10
+ @_instance_variables = exception.frame_binding.eval("instance_variables").map do |name|
11
+ name.to_s.tr("@", "")
12
+ end
13
+ end
14
+
15
+ def words
16
+ local_variable_names + method_names + instance_variable_names
17
+ end
18
+
19
+ alias target_word name
20
+
21
+ def local_variable_names
22
+ _local_variables.map {|word| LocalVariableName.new(word.to_s) }
23
+ end
24
+
25
+ def similar_local_variables
26
+ similar_words.select{|word| word.is_a?(LocalVariableName) }
27
+ end
28
+
29
+ def method_names
30
+ _methods.map {|word| MethodName.new(word.to_s) }
31
+ end
32
+
33
+ def similar_methods
34
+ similar_words.select{|word| word.is_a?(MethodName) }
35
+ end
36
+
37
+ def instance_variable_names
38
+ _instance_variables.map {|word| InstanceVariableName.new(word.to_s) }
39
+ end
40
+
41
+ def similar_instance_variables
42
+ similar_words.select {|word| word.is_a?(InstanceVariableName) }
43
+ end
44
+
45
+ def format(word)
46
+ "#{word.prefix}#{word}"
47
+ end
48
+
49
+ class MethodName < String
50
+ def prefix; "#"; end
51
+ end
52
+
53
+ class LocalVariableName < String
54
+ def prefix; ""; end
55
+ end
56
+
57
+ class InstanceVariableName < String
58
+ def prefix; "@"; end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,26 @@
1
+ module DidYouMean
2
+ class SimilarAttributeFinder
3
+ include BaseFinder
4
+ attr_reader :columns, :attribute_name
5
+
6
+ def initialize(exception)
7
+ @columns = exception.frame_binding.eval("self.class").columns
8
+ @attribute_name = (/unknown attribute(: | ')(\w+)/ =~ exception.original_message) && $2
9
+ end
10
+
11
+ def words
12
+ columns.map(&:name)
13
+ end
14
+
15
+ alias target_word attribute_name
16
+
17
+ def format(column_name)
18
+ "%{column}: %{type}" % {
19
+ column: column_name,
20
+ type: columns.detect{|c| c.name == column_name }.type
21
+ }
22
+ end
23
+ end
24
+
25
+ finders["ActiveRecord::UnknownAttributeError"] = SimilarAttributeFinder
26
+ end
@@ -0,0 +1,45 @@
1
+ module DidYouMean
2
+ class SimilarMethodFinder
3
+ include BaseFinder
4
+ attr_reader :method_name, :receiver
5
+
6
+ def initialize(exception)
7
+ @method_name, @receiver = exception.name, exception.receiver
8
+ end
9
+
10
+ def words
11
+ method_names = receiver.methods + receiver.singleton_methods
12
+ method_names.delete(@method_name)
13
+ method_names.uniq
14
+ end
15
+
16
+ alias target_word method_name
17
+
18
+ def format(word)
19
+ "#{separator}#{word}"
20
+ end
21
+
22
+ def class_method?
23
+ receiver.is_a? Class
24
+ end
25
+
26
+ def separator
27
+ class_method? ? "." : "#"
28
+ end
29
+ end
30
+
31
+ if defined?(RUBY_ENGINE)
32
+ finders["NoMethodError"] = SimilarMethodFinder
33
+
34
+ case RUBY_ENGINE
35
+ when 'ruby'
36
+ require 'did_you_mean/method_missing'
37
+ when 'jruby'
38
+ require 'did_you_mean/receiver_capturer'
39
+ org.yukinishijima.ReceiverCapturer.setup(JRuby.runtime)
40
+ NoMethodError.send(:attr, :receiver)
41
+ else
42
+ finders.delete("NoMethodError")
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,49 @@
1
+ module DidYouMean
2
+ module Levenshtein # :nodoc:
3
+ # This code is based directly on the Text gem implementation
4
+ # Returns a value representing the "cost" of transforming str1 into str2
5
+ def distance(str1, str2)
6
+ n = str1.length
7
+ m = str2.length
8
+ return m if n.zero?
9
+ return n if m.zero?
10
+
11
+ d = (0..m).to_a
12
+ x = nil
13
+
14
+ str1.each_char.with_index(1) do |char1, i|
15
+ str2.each_char.with_index do |char2, j|
16
+ cost = (char1 == char2) ? 0 : 1
17
+ x = min3(
18
+ d[j+1] + 1, # insertion
19
+ i + 1, # deletion
20
+ d[j] + cost # substitution
21
+ )
22
+ d[j] = i
23
+ i = x
24
+ end
25
+ d[m] = x
26
+ end
27
+
28
+ x
29
+ end
30
+ module_function :distance
31
+
32
+ private
33
+
34
+ # detects the minimum value out of three arguments. This method is
35
+ # faster than `[a, b, c].min` and puts less GC pressure.
36
+ # See https://github.com/yuki24/did_you_mean/pull/1 for a performance
37
+ # benchmark.
38
+ def min3(a, b, c)
39
+ if a < b && a < c
40
+ a
41
+ elsif b < c
42
+ b
43
+ else
44
+ c
45
+ end
46
+ end
47
+ module_function :min3
48
+ end
49
+ end
@@ -0,0 +1,7 @@
1
+ module DidYouMean
2
+ module TestHelper
3
+ def assert_suggestion(array, expected)
4
+ assert_equal [expected], array, "Expected #{array.inspect} to only include #{expected.inspect}"
5
+ end
6
+ end
7
+ end