did_you_mean 0.9.6-java

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.
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