reek 1.2.7.1 → 1.2.7.2

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 (68) hide show
  1. data/History.txt +13 -1
  2. data/config/defaults.reek +4 -1
  3. data/features/masking_smells.feature +7 -7
  4. data/features/rake_task.feature +2 -2
  5. data/features/reports.feature +3 -3
  6. data/features/samples.feature +5 -2
  7. data/features/yaml.feature +0 -39
  8. data/lib/reek.rb +1 -1
  9. data/lib/reek/cli/command_line.rb +3 -3
  10. data/lib/reek/cli/reek_command.rb +5 -6
  11. data/lib/reek/cli/report.rb +9 -20
  12. data/lib/reek/cli/yaml_command.rb +1 -1
  13. data/lib/reek/core/class_context.rb +1 -2
  14. data/lib/reek/core/code_context.rb +10 -27
  15. data/lib/reek/core/code_parser.rb +1 -18
  16. data/lib/reek/core/detector_stack.rb +4 -0
  17. data/lib/reek/core/masking_collection.rb +6 -0
  18. data/lib/reek/core/method_context.rb +8 -56
  19. data/lib/reek/core/module_context.rb +6 -32
  20. data/lib/reek/core/object_refs.rb +36 -36
  21. data/lib/reek/core/singleton_method_context.rb +10 -21
  22. data/lib/reek/core/sniffer.rb +3 -2
  23. data/lib/reek/examiner.rb +39 -31
  24. data/lib/reek/smell_warning.rb +8 -0
  25. data/lib/reek/smells/attribute.rb +4 -2
  26. data/lib/reek/smells/class_variable.rb +3 -3
  27. data/lib/reek/smells/control_couple.rb +1 -2
  28. data/lib/reek/smells/data_clump.rb +86 -9
  29. data/lib/reek/smells/duplication.rb +2 -3
  30. data/lib/reek/smells/feature_envy.rb +9 -4
  31. data/lib/reek/smells/simulated_polymorphism.rb +1 -2
  32. data/lib/reek/smells/smell_detector.rb +0 -6
  33. data/lib/reek/smells/uncommunicative_method_name.rb +8 -2
  34. data/lib/reek/smells/uncommunicative_parameter_name.rb +1 -1
  35. data/lib/reek/smells/uncommunicative_variable_name.rb +1 -1
  36. data/lib/reek/smells/utility_function.rb +17 -5
  37. data/lib/reek/source/reference_collector.rb +21 -0
  38. data/lib/reek/source/sexp_formatter.rb +1 -0
  39. data/lib/reek/source/tree_dresser.rb +67 -9
  40. data/lib/reek/spec/should_reek.rb +1 -1
  41. data/lib/reek/spec/should_reek_of.rb +1 -1
  42. data/lib/reek/spec/should_reek_only_of.rb +1 -1
  43. data/reek.gemspec +3 -3
  44. data/spec/reek/cli/reek_command_spec.rb +3 -2
  45. data/spec/reek/cli/report_spec.rb +2 -2
  46. data/spec/reek/cli/yaml_command_spec.rb +2 -2
  47. data/spec/reek/core/code_context_spec.rb +39 -54
  48. data/spec/reek/core/method_context_spec.rb +7 -26
  49. data/spec/reek/core/module_context_spec.rb +0 -15
  50. data/spec/reek/core/singleton_method_context_spec.rb +0 -6
  51. data/spec/reek/examiner_spec.rb +6 -6
  52. data/spec/reek/smells/attribute_spec.rb +30 -32
  53. data/spec/reek/smells/class_variable_spec.rb +15 -18
  54. data/spec/reek/smells/data_clump_spec.rb +22 -6
  55. data/spec/reek/smells/duplication_spec.rb +33 -19
  56. data/spec/reek/smells/feature_envy_spec.rb +82 -88
  57. data/spec/reek/smells/large_class_spec.rb +1 -1
  58. data/spec/reek/smells/smell_detector_shared.rb +1 -1
  59. data/spec/reek/smells/uncommunicative_method_name_spec.rb +37 -35
  60. data/spec/reek/smells/utility_function_spec.rb +7 -0
  61. data/spec/reek/source/reference_collector_spec.rb +53 -0
  62. data/spec/reek/source/tree_dresser_spec.rb +10 -0
  63. data/spec/reek/spec/should_reek_only_of_spec.rb +1 -1
  64. data/spec/spec_helper.rb +7 -0
  65. metadata +4 -5
  66. data/features/profile.feature +0 -34
  67. data/lib/reek/core/block_context.rb +0 -18
  68. data/spec/reek/core/block_context_spec.rb +0 -26
data/History.txt CHANGED
@@ -1,4 +1,16 @@
1
- == 1.2.7.1 (in development)
1
+ == 1.2.8 (in development)
2
+
3
+ == 1.2.7.2 (2010-03-05)
4
+
5
+ === Minor Changes
6
+ * Number of masked smells is no longer shown in report headers
7
+ * Masked smells are no longer listed in --yaml reports
8
+ * DataClump no longer needs infinite memory for large classes (#57)
9
+ * DataClump reports the names of the offending methods in the YAML report
10
+ * UncommunicativeMethodName now accepts operator names (+, -, ...)
11
+ * Uncommunicative Name now warns about uppercase letters in method & var names
12
+
13
+ == 1.2.7.1 (2010-02-03)
2
14
 
3
15
  === Minor Changes
4
16
  * Fixed crash on a case statement with no condition (#58)
data/config/defaults.reek CHANGED
@@ -8,6 +8,7 @@ UncommunicativeParameterName:
8
8
  reject:
9
9
  - !ruby/regexp /^.$/
10
10
  - !ruby/regexp /[0-9]$/
11
+ - !ruby/regexp /[A-Z]/
11
12
  LargeClass:
12
13
  max_methods: 25
13
14
  exclude: []
@@ -21,8 +22,9 @@ UncommunicativeMethodName:
21
22
 
22
23
  enabled: true
23
24
  reject:
24
- - !ruby/regexp /^.$/
25
+ - !ruby/regexp /^[a-z]$/
25
26
  - !ruby/regexp /[0-9]$/
27
+ - !ruby/regexp /[A-Z]/
26
28
  LongParameterList:
27
29
  max_params: 3
28
30
  exclude: []
@@ -84,6 +86,7 @@ UncommunicativeVariableName:
84
86
  reject:
85
87
  - !ruby/regexp /^.$/
86
88
  - !ruby/regexp /[0-9]$/
89
+ - !ruby/regexp /[A-Z]/
87
90
  SimulatedPolymorphism:
88
91
  exclude: []
89
92
 
@@ -41,7 +41,7 @@ Feature: Masking smells using config files
41
41
  Then the exit status indicates smells
42
42
  And it reports:
43
43
  """
44
- spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
44
+ spec/samples/masked/dirty.rb -- 3 warnings:
45
45
  Dirty#a calls @s.title twice (Duplication)
46
46
  Dirty#a calls puts(@s.title) twice (Duplication)
47
47
  Dirty#a contains iterators nested 2 deep (Nested Iterators)
@@ -54,7 +54,7 @@ Feature: Masking smells using config files
54
54
  Then the exit status indicates smells
55
55
  And it reports:
56
56
  """
57
- spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
57
+ spec/samples/masked/dirty.rb -- 3 warnings:
58
58
  Dirty#a calls @s.title twice (Duplication)
59
59
  Dirty#a calls puts(@s.title) twice (Duplication)
60
60
  Dirty#a contains iterators nested 2 deep (Nested Iterators)
@@ -66,7 +66,7 @@ Feature: Masking smells using config files
66
66
  Then the exit status indicates smells
67
67
  And it reports:
68
68
  """
69
- spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
69
+ spec/samples/masked/dirty.rb -- 6 warnings:
70
70
  (masked) Dirty has the variable name '@s' (Uncommunicative Name)
71
71
  Dirty#a calls @s.title twice (Duplication)
72
72
  Dirty#a calls puts(@s.title) twice (Duplication)
@@ -81,7 +81,7 @@ Feature: Masking smells using config files
81
81
  Then the exit status indicates smells
82
82
  And it reports:
83
83
  """
84
- spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
84
+ spec/samples/masked/dirty.rb -- 3 warnings:
85
85
  Dirty#a calls @s.title twice (Duplication)
86
86
  Dirty#a calls puts(@s.title) twice (Duplication)
87
87
  Dirty#a contains iterators nested 2 deep (Nested Iterators)
@@ -93,7 +93,7 @@ Feature: Masking smells using config files
93
93
  Then the exit status indicates smells
94
94
  And it reports:
95
95
  """
96
- spec/samples/not_quite_masked/dirty.rb -- 5 warnings (+1 masked):
96
+ spec/samples/not_quite_masked/dirty.rb -- 5 warnings:
97
97
  Dirty has the variable name '@s' (Uncommunicative Name)
98
98
  Dirty#a calls @s.title twice (Duplication)
99
99
  Dirty#a calls puts(@s.title) twice (Duplication)
@@ -108,7 +108,7 @@ Feature: Masking smells using config files
108
108
  Then the exit status indicates smells
109
109
  And it reports:
110
110
  """
111
- spec/samples/overrides/masked/dirty.rb -- 2 warnings (+4 masked):
111
+ spec/samples/overrides/masked/dirty.rb -- 2 warnings:
112
112
  Dirty#a calls @s.title twice (Duplication)
113
113
  Dirty#a calls puts(@s.title) twice (Duplication)
114
114
 
@@ -120,7 +120,7 @@ Feature: Masking smells using config files
120
120
  Then the exit status indicates smells
121
121
  And it reports:
122
122
  """
123
- spec/samples/overrides/masked/dirty.rb -- 2 warnings (+4 masked):
123
+ spec/samples/overrides/masked/dirty.rb -- 6 warnings:
124
124
  (masked) Dirty has the variable name '@s' (Uncommunicative Name)
125
125
  Dirty#a calls @s.title twice (Duplication)
126
126
  Dirty#a calls puts(@s.title) twice (Duplication)
@@ -13,7 +13,7 @@ Feature: Reek can be driven through its Task
13
13
  Then the exit status indicates an error
14
14
  And it reports:
15
15
  """
16
- spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
16
+ spec/samples/masked/dirty.rb -- 3 warnings:
17
17
  Dirty#a calls @s.title twice (Duplication)
18
18
  Dirty#a calls puts(@s.title) twice (Duplication)
19
19
  Dirty#a contains iterators nested 2 deep (Nested Iterators)
@@ -29,7 +29,7 @@ Feature: Reek can be driven through its Task
29
29
  Then the exit status indicates an error
30
30
  And it reports:
31
31
  """
32
- spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
32
+ spec/samples/masked/dirty.rb -- 3 warnings:
33
33
  Dirty#a calls @s.title twice (Duplication)
34
34
  Dirty#a calls puts(@s.title) twice (Duplication)
35
35
  Dirty#a contains iterators nested 2 deep (Nested Iterators)
@@ -60,10 +60,10 @@ Feature: Correctly formatted reports
60
60
 
61
61
  Scenario Outline: -a turns on details in presence of -q
62
62
  When I run reek <options> spec/samples/clean_due_to_masking/*.rb
63
- Then it succeeds
63
+ Then the exit status indicates smells
64
64
  And it reports:
65
65
  """
66
- spec/samples/clean_due_to_masking/dirty_one.rb -- 0 warnings (+7 masked):
66
+ spec/samples/clean_due_to_masking/dirty_one.rb -- 7 warnings:
67
67
  (masked) Dirty has no descriptive comment (Irresponsible Module)
68
68
  (masked) Dirty has the variable name '@s' (Uncommunicative Name)
69
69
  (masked) Dirty#a calls @s.title twice (Duplication)
@@ -71,7 +71,7 @@ Feature: Correctly formatted reports
71
71
  (masked) Dirty#a contains iterators nested 2 deep (Nested Iterators)
72
72
  (masked) Dirty#a has the name 'a' (Uncommunicative Name)
73
73
  (masked) Dirty#a has the variable name 'x' (Uncommunicative Name)
74
- spec/samples/clean_due_to_masking/dirty_two.rb -- 0 warnings (+7 masked):
74
+ spec/samples/clean_due_to_masking/dirty_two.rb -- 7 warnings:
75
75
  (masked) Dirty has no descriptive comment (Irresponsible Module)
76
76
  (masked) Dirty has the variable name '@s' (Uncommunicative Name)
77
77
  (masked) Dirty#a calls @s.title twice (Duplication)
@@ -10,7 +10,7 @@ Feature: Basic smell detection
10
10
  Then the exit status indicates smells
11
11
  And it reports:
12
12
  """
13
- spec/samples/inline.rb -- 41 warnings (+1 masked):
13
+ spec/samples/inline.rb -- 41 warnings:
14
14
  File has no descriptive comment (Irresponsible Module)
15
15
  Inline declares the class variable @@directory (Class Variable)
16
16
  Inline declares the class variable @@rootdir (Class Variable)
@@ -178,7 +178,7 @@ Feature: Basic smell detection
178
178
  Then the exit status indicates smells
179
179
  And it reports:
180
180
  """
181
- spec/samples/redcloth.rb -- 94 warnings:
181
+ spec/samples/redcloth.rb -- 97 warnings:
182
182
  RedCloth has at least 44 methods (Large Class)
183
183
  RedCloth has the variable name 'a' (Uncommunicative Name)
184
184
  RedCloth has the variable name 'b' (Uncommunicative Name)
@@ -245,6 +245,7 @@ Feature: Basic smell detection
245
245
  RedCloth#inline_textile_span contains iterators nested 2 deep (Nested Iterators)
246
246
  RedCloth#inline_textile_span has approx 8 statements (Long Method)
247
247
  RedCloth#inline_textile_span has the variable name 'm' (Uncommunicative Name)
248
+ RedCloth#lT has the name 'lT' (Uncommunicative Name)
248
249
  RedCloth#no_textile doesn't depend on instance state (Low Cohesion)
249
250
  RedCloth#no_textile refers to text more than self (Low Cohesion)
250
251
  RedCloth#pba calls $1.length twice (Duplication)
@@ -272,6 +273,8 @@ Feature: Basic smell detection
272
273
  RedCloth#textile_fn_ is controlled by argument atts (Control Couple)
273
274
  RedCloth#textile_p has 4 parameters (Long Parameter List)
274
275
  RedCloth#textile_p is controlled by argument atts (Control Couple)
276
+ RedCloth#textile_popup_help has the parameter name 'windowH' (Uncommunicative Name)
277
+ RedCloth#textile_popup_help has the parameter name 'windowW' (Uncommunicative Name)
275
278
  RedCloth#to_html has approx 24 statements (Long Method)
276
279
 
277
280
  """
@@ -15,19 +15,6 @@ Feature: Report smells using simple YAML layout
15
15
  And it reports:
16
16
  """
17
17
  ---
18
- - !ruby/object:Reek::SmellWarning
19
- location:
20
- lines:
21
- - 5
22
- context: Dirty
23
- source: spec/samples/masked/dirty.rb
24
- smell:
25
- class: UncommunicativeName
26
- subclass: UncommunicativeVariableName
27
- variable_name: "@s"
28
- message: has the variable name '@s'
29
- status:
30
- is_active: false
31
18
  - !ruby/object:Reek::SmellWarning
32
19
  location:
33
20
  lines:
@@ -71,32 +58,6 @@ Feature: Report smells using simple YAML layout
71
58
  message: contains iterators nested 2 deep
72
59
  status:
73
60
  is_active: true
74
- - !ruby/object:Reek::SmellWarning
75
- location:
76
- lines:
77
- - 3
78
- context: Dirty#a
79
- source: spec/samples/masked/dirty.rb
80
- smell:
81
- method_name: a
82
- class: UncommunicativeName
83
- subclass: UncommunicativeMethodName
84
- message: has the name 'a'
85
- status:
86
- is_active: false
87
- - !ruby/object:Reek::SmellWarning
88
- location:
89
- lines:
90
- - 5
91
- context: Dirty#a
92
- source: spec/samples/masked/dirty.rb
93
- smell:
94
- class: UncommunicativeName
95
- subclass: UncommunicativeVariableName
96
- variable_name: x
97
- message: has the variable name 'x'
98
- status:
99
- is_active: false
100
61
 
101
62
  """
102
63
 
data/lib/reek.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  # Reek's core functionality
3
3
  #
4
4
  module Reek
5
- VERSION = '1.2.7.1'
5
+ VERSION = '1.2.7.2'
6
6
  end
7
7
 
8
8
  require File.join(File.dirname(File.expand_path(__FILE__)), 'reek', 'examiner')
@@ -18,7 +18,7 @@ module Reek
18
18
  @argv = argv
19
19
  @parser = OptionParser.new
20
20
  @report_class = VerboseReport
21
- @show_all = false
21
+ @collection_strategy = ActiveSmellsOnly
22
22
  @command_class = ReekCommand
23
23
  set_options
24
24
  end
@@ -66,7 +66,7 @@ EOB
66
66
 
67
67
  @parser.separator "\nReport formatting:"
68
68
  @parser.on("-a", "--[no-]show-all", "Show all smells, including those masked by config settings") do |opt|
69
- @show_all = opt
69
+ @collection_strategy = opt ? ActiveAndMaskedSmells : ActiveSmellsOnly
70
70
  end
71
71
  @parser.on("-q", "--[no-]quiet", "Suppress headings for smell-free source files") do |opt|
72
72
  @report_class = opt ? QuietReport : VerboseReport
@@ -90,7 +90,7 @@ EOB
90
90
  YamlCommand.create(sources)
91
91
  else
92
92
  sources = get_sources
93
- ReekCommand.create(sources, @report_class, @show_all)
93
+ ReekCommand.create(sources, @report_class, @collection_strategy)
94
94
  end
95
95
  end
96
96
 
@@ -8,21 +8,20 @@ module Reek
8
8
  # text report format.
9
9
  #
10
10
  class ReekCommand
11
- def self.create(sources, report_class, show_all)
12
- examiners = sources.map {|src| Examiner.new(src) }
13
- new(examiners, report_class, show_all)
11
+ def self.create(sources, report_class, strategy = ActiveSmellsOnly)
12
+ examiners = sources.map { |src| Examiner.new(src, strategy.new) }
13
+ new(examiners, report_class)
14
14
  end
15
15
 
16
- def initialize(examiners, report_class, show_all)
16
+ def initialize(examiners, report_class)
17
17
  @examiners = examiners
18
18
  @report_class = report_class
19
- @show_all = show_all #SMELL: boolean parameter
20
19
  end
21
20
 
22
21
  def execute(view)
23
22
  had_smells = false
24
23
  @examiners.each do |examiner|
25
- rpt = @report_class.new(examiner, @show_all)
24
+ rpt = @report_class.new(examiner)
26
25
  had_smells ||= examiner.smelly?
27
26
  view.output(rpt.report)
28
27
  end
@@ -10,9 +10,8 @@ module Reek
10
10
 
11
11
  SMELL_FORMAT = '%m%c %w (%s)'
12
12
 
13
- def initialize(examiner, display_masked_warnings)
13
+ def initialize(examiner)
14
14
  @examiner = examiner
15
- @display_masked_warnings = display_masked_warnings # SMELL: Control Couple
16
15
  end
17
16
 
18
17
  # Creates a formatted report of all the +Smells::SmellWarning+ objects recorded in
@@ -31,45 +30,35 @@ module Reek
31
30
  end
32
31
 
33
32
  def header
34
- "#{@examiner.description} -- #{visible_header}#{masked_header}"
33
+ "#{@examiner.description} -- #{visible_header}"
35
34
  end
36
35
 
37
36
  # Creates a formatted report of all the +Smells::SmellWarning+ objects recorded in
38
37
  # this report.
39
38
  def smell_list
40
- if @display_masked_warnings
41
- result = @examiner.all_smells.map {|smell| " #{smell.report(SMELL_FORMAT)}"}
42
- else
43
- result = @examiner.all_active_smells.map {|smell| " #{smell.report(SMELL_FORMAT)}"}
44
- end
45
- result.join("\n")
39
+ @examiner.smells.map {|smell| " #{smell.report(SMELL_FORMAT)}"}.join("\n")
46
40
  end
47
41
 
48
42
  private
49
43
 
50
44
  def should_report
51
- @examiner.num_active_smells > 0 or (@display_masked_warnings and @examiner.num_masked_smells > 0)
45
+ @examiner.num_smells > 0
52
46
  end
53
47
 
54
48
  def visible_header
55
- num_smells = @examiner.all_active_smells.length
49
+ num_smells = @examiner.smells.length
56
50
  result = "#{num_smells} warning"
57
51
  result += 's' unless num_smells == 1
58
52
  result
59
53
  end
60
-
61
- def masked_header
62
- num_masked_warnings = @examiner.num_masked_smells
63
- num_masked_warnings == 0 ? '' : " (+#{num_masked_warnings} masked)"
64
- end
65
54
  end
66
55
 
67
56
  #
68
57
  # A report that lists every source, including those that have no smells.
69
58
  #
70
59
  class VerboseReport
71
- def initialize(examiner, display_masked_warnings)
72
- @reporter = ReportSection.new(examiner, display_masked_warnings)
60
+ def initialize(examiner)
61
+ @reporter = ReportSection.new(examiner)
73
62
  end
74
63
  def report
75
64
  @reporter.verbose_report
@@ -80,8 +69,8 @@ module Reek
80
69
  # A report that lists a section for each source that has smells.
81
70
  #
82
71
  class QuietReport
83
- def initialize(examiner, display_masked_warnings)
84
- @reporter = ReportSection.new(examiner, display_masked_warnings)
72
+ def initialize(examiner)
73
+ @reporter = ReportSection.new(examiner)
85
74
  end
86
75
  def report
87
76
  @reporter.quiet_report
@@ -19,7 +19,7 @@ module Reek
19
19
 
20
20
  def execute(view)
21
21
  smells = []
22
- @examiners.each {|examiner| smells += examiner.all_smells}
22
+ @examiners.each {|examiner| smells += examiner.smells}
23
23
  if smells.empty?
24
24
  view.report_success
25
25
  else
@@ -12,11 +12,10 @@ module Reek
12
12
 
13
13
  def initialize(outer, name, exp)
14
14
  super
15
- @superclass = exp[2]
16
15
  end
17
16
 
18
17
  def is_struct?
19
- @superclass == [:const, :Struct]
18
+ exp.superclass == [:const, :Struct]
20
19
  end
21
20
  end
22
21
  end
@@ -1,13 +1,4 @@
1
1
 
2
- #
3
- # Extensions to +Module+ needed by Reek.
4
- #
5
- class Module
6
- def const_or_nil(sym)
7
- const_defined?(sym) ? const_get(sym) : nil
8
- end
9
- end
10
-
11
2
  module Reek
12
3
  module Core
13
4
 
@@ -19,12 +10,15 @@ module Reek
19
10
  #
20
11
  class CodeContext
21
12
 
22
- attr_reader :name, :exp
13
+ attr_reader :exp
23
14
 
24
15
  def initialize(outer, exp)
25
16
  @outer = outer
26
17
  @exp = exp
27
- @scope_connector = ''
18
+ end
19
+
20
+ def name
21
+ @exp.name
28
22
  end
29
23
 
30
24
  def local_nodes(type, &blk)
@@ -32,22 +26,12 @@ module Reek
32
26
  end
33
27
 
34
28
  def each_node(type, ignoring, &blk)
35
- if block_given?
36
- @exp.look_for(type, ignoring, &blk)
37
- else
38
- result = []
39
- @exp.look_for(type, ignoring) {|exp| result << exp}
40
- result
41
- end
29
+ @exp.each_node(type, ignoring, &blk)
42
30
  end
43
31
 
44
- # SMELL: Temporary Field -- @name isn't always initialized
45
- def matches?(strings)
46
- me = @name.to_s
47
- strings.any? do |str|
48
- re = /#{str}/
49
- re === me or re === self.full_name
50
- end
32
+ def matches?(candidates)
33
+ my_fq_name = full_name
34
+ candidates.any? {|str| /#{str}/ === my_fq_name }
51
35
  end
52
36
 
53
37
  #
@@ -64,8 +48,7 @@ module Reek
64
48
 
65
49
  def full_name
66
50
  outer = @outer ? @outer.full_name : ''
67
- prefix = outer == '' ? '' : "#{outer}#{@scope_connector}"
68
- "#{prefix}#{@name}"
51
+ exp.full_name(outer)
69
52
  end
70
53
  end
71
54
  end