reek 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. data/History.txt +44 -4
  2. data/License.txt +20 -0
  3. data/README.rdoc +83 -0
  4. data/Rakefile +0 -1
  5. data/bin/reek +3 -11
  6. data/config/defaults.reek +20 -1
  7. data/features/masking_smells.feature +111 -0
  8. data/features/options.feature +49 -0
  9. data/features/reports.feature +90 -0
  10. data/features/samples.feature +284 -0
  11. data/features/stdin.feature +43 -0
  12. data/features/step_definitions/reek_steps.rb +35 -0
  13. data/features/support/env.rb +38 -0
  14. data/lib/reek.rb +1 -1
  15. data/lib/reek/adapters/application.rb +47 -0
  16. data/lib/reek/adapters/config_file.rb +31 -0
  17. data/lib/reek/adapters/core_extras.rb +72 -0
  18. data/lib/reek/{object_source.rb → adapters/object_source.rb} +15 -19
  19. data/lib/reek/{rake_task.rb → adapters/rake_task.rb} +2 -2
  20. data/lib/reek/adapters/report.rb +91 -0
  21. data/lib/reek/adapters/source.rb +53 -0
  22. data/lib/reek/{spec.rb → adapters/spec.rb} +45 -60
  23. data/lib/reek/block_context.rb +1 -1
  24. data/lib/reek/class_context.rb +26 -6
  25. data/lib/reek/code_context.rb +8 -0
  26. data/lib/reek/code_parser.rb +82 -39
  27. data/lib/reek/command_line.rb +85 -0
  28. data/lib/reek/configuration.rb +51 -0
  29. data/lib/reek/detector_stack.rb +39 -0
  30. data/lib/reek/exceptions.reek +8 -1
  31. data/lib/reek/method_context.rb +53 -11
  32. data/lib/reek/module_context.rb +1 -2
  33. data/lib/reek/name.rb +8 -2
  34. data/lib/reek/sexp_formatter.rb +2 -0
  35. data/lib/reek/smell_warning.rb +26 -8
  36. data/lib/reek/smells/control_couple.rb +8 -4
  37. data/lib/reek/smells/data_clump.rb +88 -0
  38. data/lib/reek/smells/duplication.rb +11 -9
  39. data/lib/reek/smells/feature_envy.rb +3 -4
  40. data/lib/reek/smells/large_class.rb +17 -17
  41. data/lib/reek/smells/long_method.rb +10 -8
  42. data/lib/reek/smells/long_parameter_list.rb +16 -10
  43. data/lib/reek/smells/long_yield_list.rb +1 -1
  44. data/lib/reek/smells/nested_iterators.rb +3 -3
  45. data/lib/reek/smells/simulated_polymorphism.rb +58 -0
  46. data/lib/reek/smells/smell_detector.rb +94 -27
  47. data/lib/reek/smells/uncommunicative_name.rb +23 -23
  48. data/lib/reek/smells/utility_function.rb +27 -11
  49. data/lib/reek/sniffer.rb +183 -0
  50. data/reek.gemspec +5 -5
  51. data/spec/quality/reek_source_spec.rb +15 -0
  52. data/spec/reek/adapters/report_spec.rb +49 -0
  53. data/spec/reek/adapters/should_reek_of_spec.rb +108 -0
  54. data/spec/reek/adapters/should_reek_only_of_spec.rb +87 -0
  55. data/spec/reek/adapters/should_reek_spec.rb +92 -0
  56. data/spec/reek/block_context_spec.rb +7 -1
  57. data/spec/reek/class_context_spec.rb +39 -16
  58. data/spec/reek/code_context_spec.rb +7 -7
  59. data/spec/reek/code_parser_spec.rb +6 -1
  60. data/spec/reek/config_spec.rb +3 -3
  61. data/spec/reek/configuration_spec.rb +12 -0
  62. data/spec/reek/method_context_spec.rb +2 -2
  63. data/spec/reek/name_spec.rb +24 -0
  64. data/spec/reek/object_source_spec.rb +23 -0
  65. data/spec/reek/singleton_method_context_spec.rb +2 -2
  66. data/spec/reek/smell_warning_spec.rb +53 -0
  67. data/spec/reek/smells/data_clump_spec.rb +87 -0
  68. data/spec/reek/smells/duplication_spec.rb +13 -17
  69. data/spec/reek/smells/feature_envy_spec.rb +23 -28
  70. data/spec/reek/smells/large_class_spec.rb +109 -34
  71. data/spec/reek/smells/long_method_spec.rb +140 -3
  72. data/spec/reek/smells/long_parameter_list_spec.rb +1 -2
  73. data/spec/reek/smells/simulated_polymorphism_spec.rb +50 -0
  74. data/spec/reek/smells/smell_detector_spec.rb +53 -0
  75. data/spec/reek/smells/uncommunicative_name_spec.rb +20 -7
  76. data/spec/reek/smells/utility_function_spec.rb +76 -67
  77. data/spec/reek/sniffer_spec.rb +10 -0
  78. data/spec/samples/all_but_one_masked/clean_one.rb +6 -0
  79. data/spec/samples/all_but_one_masked/dirty.rb +7 -0
  80. data/spec/samples/all_but_one_masked/masked.reek +5 -0
  81. data/spec/samples/clean_due_to_masking/clean_one.rb +6 -0
  82. data/spec/samples/clean_due_to_masking/clean_three.rb +6 -0
  83. data/spec/samples/clean_due_to_masking/clean_two.rb +6 -0
  84. data/spec/samples/clean_due_to_masking/dirty_one.rb +7 -0
  85. data/spec/samples/clean_due_to_masking/dirty_two.rb +7 -0
  86. data/spec/samples/clean_due_to_masking/masked.reek +7 -0
  87. data/spec/samples/corrupt_config_file/corrupt.reek +1 -0
  88. data/spec/samples/corrupt_config_file/dirty.rb +7 -0
  89. data/spec/samples/empty_config_file/dirty.rb +7 -0
  90. data/spec/samples/empty_config_file/empty.reek +0 -0
  91. data/spec/samples/exceptions.reek +4 -0
  92. data/spec/{slow/samples → samples}/inline.rb +0 -0
  93. data/spec/samples/masked/dirty.rb +7 -0
  94. data/spec/samples/masked/masked.reek +3 -0
  95. data/spec/samples/mixed_results/clean_one.rb +6 -0
  96. data/spec/samples/mixed_results/clean_three.rb +6 -0
  97. data/spec/samples/mixed_results/clean_two.rb +6 -0
  98. data/spec/samples/mixed_results/dirty_one.rb +7 -0
  99. data/spec/samples/mixed_results/dirty_two.rb +7 -0
  100. data/spec/samples/not_quite_masked/dirty.rb +8 -0
  101. data/spec/samples/not_quite_masked/masked.reek +5 -0
  102. data/spec/{slow/samples → samples}/optparse.rb +0 -0
  103. data/spec/samples/overrides/masked/dirty.rb +7 -0
  104. data/spec/samples/overrides/masked/lower.reek +5 -0
  105. data/spec/samples/overrides/upper.reek +5 -0
  106. data/spec/{slow/samples → samples}/redcloth.rb +0 -0
  107. data/spec/samples/three_clean_files/clean_one.rb +6 -0
  108. data/spec/samples/three_clean_files/clean_three.rb +6 -0
  109. data/spec/samples/three_clean_files/clean_two.rb +6 -0
  110. data/spec/samples/two_smelly_files/dirty_one.rb +7 -0
  111. data/spec/samples/two_smelly_files/dirty_two.rb +7 -0
  112. data/spec/spec.opts +1 -1
  113. data/spec/spec_helper.rb +4 -4
  114. data/tasks/reek.rake +8 -5
  115. data/tasks/test.rake +51 -0
  116. metadata +75 -25
  117. data/README.txt +0 -6
  118. data/lib/reek/options.rb +0 -92
  119. data/lib/reek/report.rb +0 -81
  120. data/lib/reek/smells/smells.rb +0 -81
  121. data/lib/reek/source.rb +0 -127
  122. data/spec/reek/options_spec.rb +0 -13
  123. data/spec/reek/report_spec.rb +0 -48
  124. data/spec/reek/smells/smell_spec.rb +0 -24
  125. data/spec/slow/inline_spec.rb +0 -43
  126. data/spec/slow/optparse_spec.rb +0 -108
  127. data/spec/slow/redcloth_spec.rb +0 -101
  128. data/spec/slow/reek_source_spec.rb +0 -20
  129. data/spec/slow/script_spec.rb +0 -55
  130. data/spec/slow/source_list_spec.rb +0 -40
  131. data/tasks/rspec.rake +0 -21
@@ -1,8 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
2
 
3
- require 'reek/code_parser'
4
3
  require 'reek/smells/long_parameter_list'
5
- require 'reek/report'
6
4
 
7
5
  include Reek
8
6
  include Reek::Smells
@@ -63,6 +61,7 @@ describe LongParameterList do
63
61
  end
64
62
 
65
63
  it 'should only report long param list' do
64
+ pending('test requires ParseTree') unless ObjectSource.can_parse_objects?
66
65
  InnerTest.should reek_only_of(:LongParameterList, /abc/)
67
66
  end
68
67
  end
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
+
3
+ require 'reek/smells/simulated_polymorphism'
4
+
5
+ include Reek::Smells
6
+
7
+ describe SimulatedPolymorphism do
8
+ it 'should report 3 similar conditions in a class' do
9
+ src = <<EOS
10
+ class Scrunch
11
+ def first
12
+ return @field == :sym ? 0 : 3;
13
+ end
14
+ def second
15
+ if @field == :sym
16
+ @other += " quarts"
17
+ end
18
+ end
19
+ def third
20
+ raise 'flu!' unless @field == :sym
21
+ end
22
+ end
23
+ EOS
24
+
25
+ src.should reek_only_of(:SimulatedPolymorphism, /@field == :sym/)
26
+ end
27
+
28
+ it 'should include case statements in the count' do
29
+ src = <<EOS
30
+ class Scrunch
31
+ def first
32
+ return @field ? 0 : 3;
33
+ end
34
+ def second
35
+ case @field
36
+ when :sym
37
+ @other += " quarts"
38
+ end
39
+ end
40
+ def third
41
+ raise 'flu!' unless @field
42
+ end
43
+ end
44
+ EOS
45
+
46
+ src.should reek_only_of(:SimulatedPolymorphism, /@field/)
47
+ end
48
+
49
+ # And count code in superclasses, if we have it
50
+ end
@@ -0,0 +1,53 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
+
3
+ require 'reek/configuration'
4
+ require 'reek/smells/duplication'
5
+ require 'reek/smells/large_class'
6
+ require 'reek/smells/long_method'
7
+
8
+ include Reek
9
+ include Reek::Smells
10
+
11
+ describe SmellDetector, 'configuration' do
12
+ before:each do
13
+ @detector = LongMethod.new
14
+ end
15
+
16
+ it 'adopts new max_statements value' do
17
+ @detector.configure_with(LongMethod::MAX_ALLOWED_STATEMENTS_KEY => 25)
18
+ @detector.value(LongMethod::MAX_ALLOWED_STATEMENTS_KEY, nil, 0).should == 25
19
+ end
20
+ end
21
+
22
+ describe SmellDetector, 'when copied' do
23
+ before :each do
24
+ @detector = LongMethod.new
25
+ @copy = @detector.copy
26
+ end
27
+
28
+ it 'should have the same state' do
29
+ @copy.max_statements.should == @detector.max_statements
30
+ end
31
+
32
+ it 'should change independently of its parent' do
33
+ default_max = @detector.max_statements
34
+ @copy.configure_with(LongMethod::MAX_ALLOWED_STATEMENTS_KEY => 25)
35
+ @detector.max_statements.should == default_max
36
+ end
37
+ end
38
+
39
+ describe SmellDetector, 'configuration' do
40
+ # it 'stays enabled when not disabled' do
41
+ # @detector = LargeClass.new
42
+ # @detector.should be_enabled
43
+ # @detector.configure({'LargeClass' => {'max_methods' => 50}})
44
+ # @detector.should be_enabled
45
+ # end
46
+
47
+ it 'becomes disabled when disabled' do
48
+ @detector = LargeClass.new
49
+ @detector.should be_enabled
50
+ @detector.configure({'LargeClass' => {SmellConfiguration::ENABLED_KEY => false}})
51
+ @detector.should_not be_enabled
52
+ end
53
+ end
@@ -1,5 +1,5 @@
1
1
  require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
- require 'ostruct'
2
+
3
3
  require 'reek/method_context'
4
4
  require 'reek/smells/uncommunicative_name'
5
5
 
@@ -16,6 +16,9 @@ describe UncommunicativeName, "method name" do
16
16
  it 'should report name of the form "x2"' do
17
17
  'def x2(fred) basics(17) end'.should reek_only_of(:UncommunicativeName, /x2/)
18
18
  end
19
+ it 'should report long name ending in a number' do
20
+ 'def method2(fred) basics(17) end'.should reek_only_of(:UncommunicativeName, /method2/)
21
+ end
19
22
  end
20
23
 
21
24
  describe UncommunicativeName, "field name" do
@@ -28,6 +31,9 @@ describe UncommunicativeName, "field name" do
28
31
  it 'should report name of the form "x2"' do
29
32
  'class Thing; def simple(fred) @x2 end end'.should reek_only_of(:UncommunicativeName, /@x2/, /Thing/, /variable name/)
30
33
  end
34
+ it 'should report long name ending in a number' do
35
+ 'class Thing; def simple(fred) @field12 end end'.should reek_only_of(:UncommunicativeName, /@field12/, /Thing/, /variable name/)
36
+ end
31
37
  it 'should report one-letter fieldname in assignment' do
32
38
  'class Thing; def simple(fred) @x = fred end end'.should reek_only_of(:UncommunicativeName, /@x/, /Thing/, /variable name/)
33
39
  end
@@ -43,6 +49,9 @@ describe UncommunicativeName, "local variable name" do
43
49
  it 'should report name of the form "x2"' do
44
50
  'def simple(fred) x2 = jim(45) end'.should reek_only_of(:UncommunicativeName, /x2/, /variable name/)
45
51
  end
52
+ it 'should report long name ending in a number' do
53
+ 'def simple(fred) var123 = jim(45) end'.should reek_only_of(:UncommunicativeName, /var123/, /variable name/)
54
+ end
46
55
  it 'should report variable name only once' do
47
56
  'def simple(fred) x = jim(45); x = y end'.should reek_only_of(:UncommunicativeName, /x/)
48
57
  end
@@ -63,6 +72,9 @@ describe UncommunicativeName, "parameter name" do
63
72
  it 'should report name of the form "x2"' do
64
73
  'def help(x2) basics(17) end'.should reek_only_of(:UncommunicativeName, /x2/, /variable name/)
65
74
  end
75
+ it 'should report long name ending in a number' do
76
+ 'def help(param1) basics(17) end'.should reek_only_of(:UncommunicativeName, /param1/, /variable name/)
77
+ end
66
78
  end
67
79
 
68
80
  describe UncommunicativeName, "block parameter name" do
@@ -78,6 +90,7 @@ def bad
78
90
  end
79
91
  end
80
92
  EOS
93
+
81
94
  src.should reek_only_of(:UncommunicativeName, /'x'/)
82
95
  end
83
96
  end
@@ -85,7 +98,7 @@ end
85
98
  describe UncommunicativeName, "several names" do
86
99
 
87
100
  it 'should report all bad names' do
88
- ruby = Source.from_s('class Oof; def y(x) @z = x end end')
101
+ ruby = 'class Oof; def y(x) @z = x end end'.sniff
89
102
  ruby.should reek_of(:UncommunicativeName, /'x'/)
90
103
  ruby.should reek_of(:UncommunicativeName, /'y'/)
91
104
  ruby.should reek_of(:UncommunicativeName, /'@z'/)
@@ -100,6 +113,7 @@ class Thing
100
113
  end
101
114
  end
102
115
  EOS
116
+
103
117
  source.should reek_of(:UncommunicativeName, /'x'/)
104
118
  source.should reek_of(:UncommunicativeName, /'y'/)
105
119
  end
@@ -107,17 +121,16 @@ end
107
121
 
108
122
  describe UncommunicativeName, '#examine' do
109
123
  before :each do
110
- @report = Report.new
111
124
  @uc = UncommunicativeName.new
112
125
  end
113
126
 
114
127
  it 'should return true when reporting a smell' do
115
- mc = MethodContext.new(StopContext.new, [:defn, :x, nil])
116
- @uc.examine(mc, @report).should == true
128
+ mc = MethodContext.new(StopContext.new, s(:defn, :x, s(:args)))
129
+ @uc.examine(mc).should == true
117
130
  end
118
131
 
119
132
  it 'should return false when not reporting a smell' do
120
- mc = MethodContext.new(StopContext.new, [:defn, :not_bad, nil])
121
- @uc.examine(mc, @report).should == false
133
+ mc = MethodContext.new(StopContext.new, s(:defn, :not_bad, s(:args)))
134
+ @uc.examine(mc).should == false
122
135
  end
123
136
  end
@@ -6,88 +6,97 @@ include Reek
6
6
  include Reek::Smells
7
7
 
8
8
  describe UtilityFunction do
9
-
10
- it 'should not report attrset' do
11
- class Fred
12
- attr_writer :xyz
9
+ context 'with no calls' do
10
+ it 'does not report empty method' do
11
+ 'def simple(arga) end'.should_not reek
12
+ end
13
+ it 'does not report literal' do
14
+ 'def simple(arga) 3; end'.should_not reek
15
+ end
16
+ it 'does not report instance variable reference' do
17
+ 'def simple(arga) @yellow end'.should_not reek
18
+ end
19
+ it 'does not report vcall' do
20
+ 'def simple(arga) y end'.should_not reek
21
+ end
22
+ it 'does not report references to self' do
23
+ 'def into; self; end'.should_not reek
24
+ end
25
+ it 'recognises an ivar reference within a block' do
26
+ 'def clean(text) text.each { @fred = 3} end'.should_not reek
13
27
  end
14
- Fred.should_not reek
15
28
  end
16
29
 
17
- it 'should count usages of self'do
18
- 'def <=>(other) Options[:sort_order].compare(self, other) end'.should_not reek
19
- end
20
- it 'should count self reference within a dstr' do
21
- 'def as(alias_name); "#{self} as #{alias_name}".to_sym; end'.should_not reek
22
- end
23
- it 'should count calls to self within a dstr' do
24
- 'def to_sql; "\'#{self.gsub(/\'/, "\'\'")}\'"; end'.should_not reek
25
- end
26
- it 'should report simple parameter call' do
27
- 'def simple(arga) arga.to_s end'.should reek_of(:UtilityFunction, /simple/)
28
- end
29
- it 'should report message chain' do
30
- 'def simple(arga) arga.b.c end'.should reek_of(:UtilityFunction, /simple/)
31
- end
32
-
33
- it 'should not report overriding methods' do
34
- class Father
35
- def thing(ff); @kids = 0; end
30
+ context 'with only one call' do
31
+ it 'does not report a call to a parameter' do
32
+ 'def simple(arga)
33
+ arga.to_s
34
+ end'.should_not reek_of(:UtilityFunction, /simple/)
36
35
  end
37
- class Son < Father
38
- def thing(ff); ff; end
36
+ it 'does not report a call to a constant' do
37
+ 'def simple(arga)
38
+ FIELDS[arga]
39
+ end'.should_not reek
39
40
  end
40
- Son.should_not reek
41
41
  end
42
42
 
43
- it 'should not report class method' do
44
- pending('bug')
45
- source = <<EOS
46
- class Cache
47
- class << self
48
- def create_unless_known(attributes)
49
- Cache.create(attributes) unless Cache.known?
43
+ context 'with two or more calls' do
44
+ it 'reports two calls' do
45
+ 'def simple(arga)
46
+ arga.to_s + arga.to_i
47
+ end'.should reek_of(:UtilityFunction, /simple/)
48
+ end
49
+ it 'should count usages of self'do
50
+ 'def <=>(other) Options[:sort_order].compare(self, other) end'.should_not reek
51
+ end
52
+ it 'should count self reference within a dstr' do
53
+ 'def as(alias_name); "#{self} as #{alias_name}".to_sym; end'.should_not reek
54
+ end
55
+ it 'should count calls to self within a dstr' do
56
+ 'def to_sql; "\'#{self.gsub(/\'/, "\'\'")}\'"; end'.should_not reek
57
+ end
58
+ it 'should report message chain' do
59
+ 'def simple(arga) arga.b.c end'.should reek_of(:UtilityFunction, /simple/)
60
+ end
61
+
62
+ it 'does not report a method that calls super' do
63
+ 'def child(arg) super; arg.to_s; end'.should_not reek
50
64
  end
51
- end
52
- end
53
- EOS
54
- source.should_not reek
55
- end
56
-
57
- it 'should recognise a deep call' do
58
- src = <<EOS
59
- class Red
60
- def deep(text)
61
- text.each { |mod| atts = shelve(mod) }
62
- end
63
65
 
64
- def shelve(val)
65
- @shelf << val
66
+ it 'should recognise a deep call' do
67
+ src = <<EOS
68
+ class Red
69
+ def deep(text)
70
+ text.each { |mod| atts = shelve(mod) }
71
+ end
72
+
73
+ def shelve(val)
74
+ @shelf << val
75
+ end
66
76
  end
67
- end
68
77
  EOS
69
- src.should_not reek
78
+ src.should_not reek
79
+ end
70
80
  end
71
81
  end
72
82
 
73
- describe UtilityFunction, 'should only report a method containing a call' do
74
- it 'should not report empty method' do
75
- 'def simple(arga) end'.should_not reek
76
- end
77
- it 'should not report literal' do
78
- 'def simple(arga) 3; end'.should_not reek
79
- end
80
- it 'should not report instance variable reference' do
81
- 'def simple(arga) @yellow end'.should_not reek
82
- end
83
- it 'should not report vcall' do
84
- 'def simple(arga) y end'.should_not reek
85
- end
86
- it 'should not report references to self' do
87
- 'def into; self; end'.should_not reek
83
+ describe UtilityFunction do
84
+ it 'should not report attrset' do
85
+ pending('test requires ParseTree') unless ObjectSource.can_parse_objects?
86
+ class Fred
87
+ attr_writer :xyz
88
+ end
89
+ Fred.should_not reek
88
90
  end
89
91
 
90
- it 'should recognise an ivar reference within a block' do
91
- 'def clean(text) text.each { @fred = 3} end'.should_not reek
92
+ it 'should not report overriding methods' do
93
+ pending('test requires ParseTree') unless ObjectSource.can_parse_objects?
94
+ class Father
95
+ def thing(ff); @kids = 0; end
96
+ end
97
+ class Son < Father
98
+ def thing(ff); ff; end
99
+ end
100
+ Son.should_not reek
92
101
  end
93
102
  end
@@ -0,0 +1,10 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ include Reek
4
+
5
+ describe Sniffer do
6
+ it 'detects smells in a file' do
7
+ dirty_file = Dir['spec/samples/two_smelly_files/*.rb'][0]
8
+ File.new(dirty_file).sniff.should be_smelly
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+ class Clean
2
+ def assign
3
+ puts @sub.title
4
+ @sub.map {|para| para.name }
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ class Dirty
2
+ def a
3
+ puts @s.title
4
+ @s.map {|x| x.each {|key| key += 3}}
5
+ puts @s.title
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ ---
2
+ Duplication:
3
+ enabled: false
4
+ UncommunicativeName:
5
+ enabled: false
@@ -0,0 +1,6 @@
1
+ class Clean
2
+ def assign
3
+ puts @sub.title
4
+ @sub.map {|para| para.name }
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class Clean
2
+ def assign
3
+ puts @sub.title
4
+ @sub.map {|para| para.name }
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class Clean
2
+ def assign
3
+ puts @sub.title
4
+ @sub.map {|para| para.name }
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ class Dirty
2
+ def a
3
+ puts @s.title
4
+ @s.map {|x| x.each {|key| key += 3}}
5
+ puts @s.title
6
+ end
7
+ end