Skip to content

Commit e2520cd

Browse files
committed
Add RuntimeMacros module
RuntimeMacros `.def_node_matcher` and `.def_node_search` accept block that will be evaluated within cop context to get pattern pattern. It can be used to pattern match values from config because are available only during runtime.
1 parent 44e7d6e commit e2520cd

File tree

4 files changed

+59
-0
lines changed

4 files changed

+59
-0
lines changed

lib/rubocop-rspec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
require_relative 'rubocop/rspec/top_level_describe'
1313
require_relative 'rubocop/rspec/wording'
1414
require_relative 'rubocop/rspec/language'
15+
require_relative 'rubocop/rspec/language/runtime_macros'
1516
require_relative 'rubocop/rspec/language/node_pattern'
1617
require_relative 'rubocop/rspec/top_level_group'
1718
require_relative 'rubocop/rspec/concept'

lib/rubocop/cop/rspec/cop.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ module RSpec
2020
class Cop < ::RuboCop::Cop::Cop
2121
include RuboCop::RSpec::Language
2222
include RuboCop::RSpec::Language::NodePattern
23+
extend RuboCop::RSpec::Language::RuntimeMacros
2324

2425
DEFAULT_CONFIGURATION =
2526
RuboCop::RSpec::CONFIG.fetch('AllCops').fetch('RSpec')

lib/rubocop/rspec/language/node_pattern.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module Language
66
# Common node matchers used for matching against the rspec DSL
77
module NodePattern
88
extend RuboCop::NodePattern::Macros
9+
extend RuntimeMacros
910

1011
def_node_matcher :example_group?, ExampleGroups::ALL.block_pattern
1112
def_node_matcher :shared_group?, SharedGroups::ALL.block_pattern
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module RSpec
5+
module Language
6+
# RuntimeMacros `.def_node_matcher` and `.def_node_search` accept block
7+
# that will be evaluated within cop context to get pattern pattern.
8+
# It can be used to pattern match values from config because are available
9+
# only during runtime.
10+
module RuntimeMacros
11+
def def_runtime_node_matcher(method_name, &pattern_block)
12+
pattern_block_constant =
13+
set_pattern_block_constant(method_name, pattern_block)
14+
15+
src = "def #{method_name}(node, *args, &block);" \
16+
'pattern = RuboCop::NodePattern.new' \
17+
"(instance_exec(&#{pattern_block_constant}));" \
18+
'match = pattern.match(node, *args, &block);' \
19+
"#{predicate_method?(method_name) ? '!match.nil?' : 'match'};" \
20+
'end'
21+
22+
location = caller_locations(1, 1).first
23+
class_eval(src, location.path, location.lineno)
24+
end
25+
26+
def def_runtime_node_search(method_name, &pattern_block)
27+
pattern_block_constant =
28+
set_pattern_block_constant(method_name, pattern_block)
29+
search_method = predicate_method?(method_name) ? ':any?' : ':select'
30+
31+
src = "def #{method_name}(node, *args, &block);" \
32+
'pattern = RuboCop::NodePattern.new' \
33+
"(instance_exec(&#{pattern_block_constant}));" \
34+
"node.each_node.public_method(#{search_method})" \
35+
'.call(&pattern.public_method(:match, *args, &block));end'
36+
37+
location = caller_locations(1, 1).first
38+
class_eval(src, location.path, location.lineno)
39+
end
40+
41+
protected
42+
43+
def set_pattern_block_constant(method_name, pattern_block)
44+
pattern_block_constant =
45+
"#{method_name.to_s.upcase.sub('?', '_PREDICATE')}_PATTERN_BLOCK"
46+
const_set(pattern_block_constant, pattern_block)
47+
pattern_block_constant
48+
end
49+
50+
def predicate_method?(method_name)
51+
method_name.to_s.end_with?('?')
52+
end
53+
end
54+
end
55+
end
56+
end

0 commit comments

Comments
 (0)