lib/suggest.rb in suggest_rb-0.3.0 vs lib/suggest.rb in suggest_rb-0.4.0
- old
+ new
@@ -45,83 +45,74 @@
[Numeric, :singleton_method_added],
[Numeric, :clone],
[Numeric, :dup],
])
- module Mixin
- def what_returns?(expected, args: [], allow_mutation: false)
- block = Proc.new if block_given?
+ SELECTOR = ->(m) do
+ SUGGEST_MODS.include?(m.owner) &&
+ !INCONSISTENT.include?([m.owner, m.name]) &&
+ !TOO_COMPLICATED.include?([m.owner, m.name])
+ end
- applicable_methods = self.methods.map(&method(:method)).select do |m|
- SUGGEST_MODS.include?(m.owner) &&
- !INCONSISTENT.include?([m.owner, m.name]) &&
- !TOO_COMPLICATED.include?([m.owner, m.name])
- end
-
- applicable_methods.select do |m|
+ module Mixin
+ def what_returns?(expected, args: [], allow_mutation: false, allow_not_public: false, &block)
+ methods.map(&method(:method)).select(&SELECTOR).select do |m|
arity = m.arity
- next unless arity == -1 || arity == args.count
+ next unless arity < 0 || arity == args.count
post = clone
- if block
- next if UNSAFE_WITH_BLOCK.include?([m.owner, m.name])
- result = post.public_send(m.name, *args, &block) rescue next
- else
- result = post.public_send(m.name, *args) rescue next
- end
+ next if block && UNSAFE_WITH_BLOCK.include?([m.owner, m.name])
+ result = post.__send__(allow_not_public ? :send : :public_send, m.name, *args, &block) rescue next
+
next unless allow_mutation || self == post
Suggest.eq?(result, expected)
end.map(&:name)
end
- def what_mutates?(expected, opts = {})
- args = opts[:args] || []
- block = Proc.new if block_given?
-
- applicable_methods = self.methods.map(&method(:method)).select do |m|
- SUGGEST_MODS.include?(m.owner) &&
- !INCONSISTENT.include?([m.owner, m.name]) &&
- !TOO_COMPLICATED.include?([m.owner, m.name])
- end
-
- applicable_methods.select do |m|
+ def what_mutates?(expected, args: [], allow_not_public: false, **opts, &block)
+ methods.map(&method(:method)).select(&SELECTOR).select do |m|
arity = m.arity
- next unless arity == -1 || arity == args.count
+ next unless arity < 0 || arity == args.count
post = clone
- if block
- next if UNSAFE_WITH_BLOCK.include?([m.owner, m.name])
- result = post.public_send(m.name, *args, &block) rescue next
- else
- result = post.public_send(m.name, *args) rescue next
- end
- if opts.key?(:returns)
- next unless Suggest.eq?(result, opts[:returns])
- end
+ next if block && UNSAFE_WITH_BLOCK.include?([m.owner, m.name])
+ result = post.__send__(allow_not_public ? :send : :public_send, m.name, *args, &block) rescue next
+ next if opts.key?(:returns) && !Suggest.eq?(result, opts[:returns])
+
Suggest.eq?(post, expected)
end.map(&:name)
end
end
def self.eq?(result, expected)
result.is_a?(expected.class) && result == expected
end
+ def self.suggestable!(mod, **corrections) # unsafe_with_block: [], inconsistent: [], too_complicated: []
+ raise ArgumentError.new("Must support smart comparison (implement «#{mod}#==»)") if mod.instance_method(:==).owner == BasicObject
+
+ SUGGEST_MODS << mod
+ %w[unsafe_with_block inconsistent too_complicated].each do |correction|
+ c = Suggest.const_get(correction.upcase)
+ [mod].product(corrections.fetch(correction, [])).each(&c.method(:<<))
+ end
+ mod.include(Suggest::Mixin) unless mod.ancestors.include?(Suggest::Mixin)
+ end
+
def self.suggestable_methods
- candidates = []
- SUGGEST_MODS.each do |mod|
+ SUGGEST_MODS.each_with_object([]) do |mod, candidates|
owned_methods = mod.instance_methods.select { |m| mod.instance_method(m).owner == mod }
next if owned_methods.none?
candidates += [mod].product(owned_methods)
- end
-
- candidates.reject do |m|
+ end.reject do |m|
INCONSISTENT.include?(m) || TOO_COMPLICATED.include?(m)
end
end
end
-Object.include(Suggest::Mixin)
+Suggest::SUGGEST_MODS.each do |mod|
+ mod.include(Suggest::Mixin) unless mod.ancestors.include?(Suggest::Mixin)
+end