inch 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/README.md +335 -3
  4. data/Rakefile +8 -0
  5. data/TODOS.md +12 -0
  6. data/bin/inch +17 -0
  7. data/inch.gemspec +7 -2
  8. data/lib/inch.rb +6 -1
  9. data/lib/inch/cli.rb +24 -0
  10. data/lib/inch/cli/arguments.rb +45 -0
  11. data/lib/inch/cli/command.rb +29 -0
  12. data/lib/inch/cli/command/base.rb +62 -0
  13. data/lib/inch/cli/command/base_list.rb +75 -0
  14. data/lib/inch/cli/command/base_object.rb +40 -0
  15. data/lib/inch/cli/command/console.rb +22 -0
  16. data/lib/inch/cli/command/inspect.rb +20 -0
  17. data/lib/inch/cli/command/list.rb +25 -0
  18. data/lib/inch/cli/command/options/base.rb +137 -0
  19. data/lib/inch/cli/command/options/base_list.rb +84 -0
  20. data/lib/inch/cli/command/options/base_object.rb +47 -0
  21. data/lib/inch/cli/command/options/console.rb +26 -0
  22. data/lib/inch/cli/command/options/inspect.rb +25 -0
  23. data/lib/inch/cli/command/options/list.rb +30 -0
  24. data/lib/inch/cli/command/options/show.rb +27 -0
  25. data/lib/inch/cli/command/options/stats.rb +20 -0
  26. data/lib/inch/cli/command/options/suggest.rb +61 -0
  27. data/lib/inch/cli/command/output/base.rb +32 -0
  28. data/lib/inch/cli/command/output/console.rb +45 -0
  29. data/lib/inch/cli/command/output/inspect.rb +129 -0
  30. data/lib/inch/cli/command/output/list.rb +87 -0
  31. data/lib/inch/cli/command/output/show.rb +79 -0
  32. data/lib/inch/cli/command/output/stats.rb +111 -0
  33. data/lib/inch/cli/command/output/suggest.rb +104 -0
  34. data/lib/inch/cli/command/show.rb +20 -0
  35. data/lib/inch/cli/command/stats.rb +20 -0
  36. data/lib/inch/cli/command/suggest.rb +104 -0
  37. data/lib/inch/cli/command_parser.rb +82 -0
  38. data/lib/inch/cli/sparkline_helper.rb +31 -0
  39. data/lib/inch/cli/trace_helper.rb +42 -0
  40. data/lib/inch/cli/yardopts_helper.rb +49 -0
  41. data/lib/inch/code_object.rb +8 -0
  42. data/lib/inch/code_object/docstring.rb +97 -0
  43. data/lib/inch/code_object/nodoc_helper.rb +84 -0
  44. data/lib/inch/code_object/proxy.rb +37 -0
  45. data/lib/inch/code_object/proxy/base.rb +194 -0
  46. data/lib/inch/code_object/proxy/class_object.rb +9 -0
  47. data/lib/inch/code_object/proxy/constant_object.rb +8 -0
  48. data/lib/inch/code_object/proxy/method_object.rb +118 -0
  49. data/lib/inch/code_object/proxy/method_parameter_object.rb +81 -0
  50. data/lib/inch/code_object/proxy/module_object.rb +8 -0
  51. data/lib/inch/code_object/proxy/namespace_object.rb +38 -0
  52. data/lib/inch/core_ext.rb +2 -0
  53. data/lib/inch/core_ext/string.rb +3 -0
  54. data/lib/inch/core_ext/yard.rb +4 -0
  55. data/lib/inch/evaluation.rb +35 -0
  56. data/lib/inch/evaluation/base.rb +60 -0
  57. data/lib/inch/evaluation/class_object.rb +6 -0
  58. data/lib/inch/evaluation/constant_object.rb +34 -0
  59. data/lib/inch/evaluation/file.rb +66 -0
  60. data/lib/inch/evaluation/method_object.rb +127 -0
  61. data/lib/inch/evaluation/module_object.rb +6 -0
  62. data/lib/inch/evaluation/namespace_object.rb +94 -0
  63. data/lib/inch/evaluation/role/base.rb +49 -0
  64. data/lib/inch/evaluation/role/constant.rb +43 -0
  65. data/lib/inch/evaluation/role/method.rb +60 -0
  66. data/lib/inch/evaluation/role/method_parameter.rb +46 -0
  67. data/lib/inch/evaluation/role/missing.rb +20 -0
  68. data/lib/inch/evaluation/role/namespace.rb +58 -0
  69. data/lib/inch/evaluation/role/object.rb +64 -0
  70. data/lib/inch/evaluation/score_range.rb +26 -0
  71. data/lib/inch/rake.rb +1 -0
  72. data/lib/inch/rake/suggest.rb +26 -0
  73. data/lib/inch/source_parser.rb +36 -0
  74. data/lib/inch/version.rb +1 -1
  75. data/test/fixtures/code_examples/lib/foo.rb +87 -0
  76. data/test/fixtures/readme/lib/foo.rb +17 -0
  77. data/test/fixtures/simple/README +25 -0
  78. data/test/fixtures/simple/lib/broken.rb +10 -0
  79. data/test/fixtures/simple/lib/foo.rb +214 -0
  80. data/test/fixtures/simple/lib/role_methods.rb +78 -0
  81. data/test/fixtures/simple/lib/role_namespaces.rb +68 -0
  82. data/test/fixtures/visibility/lib/foo.rb +18 -0
  83. data/test/fixtures/yardopts/.yardopts +1 -0
  84. data/test/fixtures/yardopts/foo/bar.rb +6 -0
  85. data/test/inch/cli/arguments_test.rb +70 -0
  86. data/test/inch/cli/command/console_test.rb +59 -0
  87. data/test/inch/cli/command/inspect_test.rb +59 -0
  88. data/test/inch/cli/command/list_test.rb +61 -0
  89. data/test/inch/cli/command/show_test.rb +59 -0
  90. data/test/inch/cli/command/stats_test.rb +57 -0
  91. data/test/inch/cli/command/suggest_test.rb +57 -0
  92. data/test/inch/cli/command_parser_test.rb +33 -0
  93. data/test/inch/cli/yardopts_helper_test.rb +84 -0
  94. data/test/inch/code_object/docstring_test.rb +204 -0
  95. data/test/inch/code_object/nodoc_helper_test.rb +38 -0
  96. data/test/inch/code_object/proxy_test.rb +188 -0
  97. data/test/inch/source_parser_test.rb +23 -0
  98. data/test/integration/stats_options_test.rb +34 -0
  99. data/test/integration/visibility_options_test.rb +79 -0
  100. data/test/test_helper.rb +21 -0
  101. metadata +184 -7
@@ -0,0 +1,57 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../test_helper')
2
+
3
+ describe ::Inch::CLI::Command::Stats do
4
+ before do
5
+ Dir.chdir fixture_path(:simple)
6
+ @command = ::Inch::CLI::Command::Stats
7
+ end
8
+
9
+ it "should run without args" do
10
+ out, err = capture_io do
11
+ @command.run()
12
+ end
13
+ refute out.empty?, "there should be some output"
14
+ assert err.empty?, "there should be no errors"
15
+ end
16
+
17
+ it "should run with filelist in args" do
18
+ out, err = capture_io do
19
+ @command.run("lib/**/*.rb", "app/**/*.rb")
20
+ end
21
+ refute out.empty?, "there should be some output"
22
+ assert err.empty?, "there should be no errors"
23
+ end
24
+
25
+ it "should run even with non-existing filelist in args" do
26
+ out, err = capture_io do
27
+ @command.run("app/**/*.rb")
28
+ end
29
+ refute out.empty?, "there should be some output"
30
+ assert err.empty?, "there should be no errors"
31
+ end
32
+
33
+ it "should output info when run with --format=json" do
34
+ out, err = capture_io do
35
+ @command.run("--format=json")
36
+ end
37
+ refute out.empty?, "there should be some output"
38
+ assert err.empty?, "there should be no errors: #{err.yellow}"
39
+ end
40
+
41
+ it "should output info when run with --format=yaml" do
42
+ out, err = capture_io do
43
+ @command.run("--format=yaml")
44
+ end
45
+ refute out.empty?, "there should be some output"
46
+ assert err.empty?, "there should be no errors: #{err.yellow}"
47
+ end
48
+
49
+ it "should output info when run with --help" do
50
+ out, err = capture_io do
51
+ assert_raises(SystemExit) { @command.run("--help") }
52
+ end
53
+ refute out.empty?, "there should be some output"
54
+ assert_match /\bUsage\b.+stats/, out
55
+ assert err.empty?, "there should be no errors"
56
+ end
57
+ end
@@ -0,0 +1,57 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../test_helper')
2
+
3
+ describe ::Inch::CLI::Command::Suggest do
4
+ before do
5
+ Dir.chdir fixture_path(:simple)
6
+ @command = ::Inch::CLI::Command::Suggest
7
+ end
8
+
9
+ it "should run without args" do
10
+ out, err = capture_io do
11
+ @command.run()
12
+ end
13
+ refute out.empty?, "there should be some output"
14
+ assert err.empty?, "there should be no errors"
15
+ assert_match /\bFoo::Bar#method_with_wrong_doc\b/, out
16
+ assert_match /\bFoo::Bar#method_without_docstring\b/, out
17
+ assert_match /\bFoo::Bar#method_with_unstructured_doc\b/, out
18
+ end
19
+
20
+ it "should run with --pedantic switch" do
21
+ out, err = capture_io do
22
+ @command.run("--pedantic")
23
+ end
24
+ refute out.empty?, "there should be some output"
25
+ assert err.empty?, "there should be no errors"
26
+ end
27
+
28
+ it "should run with filelist in args" do
29
+ out, err = capture_io do
30
+ @command.run("lib/**/*.rb", "app/**/*.rb")
31
+ end
32
+ refute out.empty?, "there should be some output"
33
+ assert err.empty?, "there should be no errors"
34
+ assert_match /\bFoo::Bar#method_with_wrong_doc\b/, out
35
+ assert_match /\bFoo::Bar#method_without_docstring\b/, out
36
+ assert_match /\bFoo::Bar#method_with_unstructured_doc\b/, out
37
+ end
38
+
39
+ it "should run with non-existing filelist in args" do
40
+ out, err = capture_io do
41
+ @command.run("app/**/*.rb")
42
+ end
43
+ # TODO: not sure what should actually happen here:
44
+ # no output or error message?
45
+ #assert out.empty?, "there should be no output"
46
+ #assert err.empty?, "there should be no errors"
47
+ end
48
+
49
+ it "should output info when run with --help" do
50
+ out, err = capture_io do
51
+ assert_raises(SystemExit) { @command.run("--help") }
52
+ end
53
+ refute out.empty?, "there should be some output"
54
+ assert_match /\bUsage\b.+suggest/, out
55
+ assert err.empty?, "there should be no errors"
56
+ end
57
+ end
@@ -0,0 +1,33 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
2
+
3
+ describe ::Inch::CLI::CommandParser do
4
+ before do
5
+ Dir.chdir fixture_path(:simple)
6
+ @command = ::Inch::CLI::CommandParser
7
+ end
8
+
9
+ it "should run without args" do
10
+ out, err = capture_io do
11
+ @command.run()
12
+ end
13
+ refute out.empty?, "there should be some output"
14
+ assert err.empty?, "there should be no errors"
15
+ end
16
+
17
+ it "should run Command::Suggest with filelist in args" do
18
+ out, err = capture_io do
19
+ @command.run("suggest", "lib/**/*.rb", "app/**/*.rb")
20
+ end
21
+ refute out.empty?, "there should be some output"
22
+ assert err.empty?, "there should be no errors"
23
+ end
24
+
25
+ it "should output info when run with --help" do
26
+ out, err = capture_io do
27
+ @command.run("--help")
28
+ end
29
+ refute out.empty?, "there should be some output"
30
+ assert_match /\bUsage\b.+inch/, out
31
+ assert err.empty?, "there should be no errors"
32
+ end
33
+ end
@@ -0,0 +1,84 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
2
+
3
+ describe ::Inch::CLI::YardoptsHelper do
4
+ before do
5
+ Dir.chdir fixture_path(:yardopts)
6
+ assert File.file?(".yardopts")
7
+ @command = ::Inch::CLI::Command::List
8
+ end
9
+
10
+ it "should run without args" do
11
+ out, err = capture_io do
12
+ @command.run()
13
+ end
14
+ refute out.empty?, "there should be some output"
15
+ assert err.empty?, "there should be no errors"
16
+ assert_match /\bFoo\b/, out
17
+ assert_match /\bFoo::Bar\b/, out
18
+ assert_match /\bFoo::Bar#initialize\b/, out
19
+ end
20
+
21
+ it "should run with --no-yardopts" do
22
+ out, err = capture_io do
23
+ @instance = @command.run("app/**/*.rb", "--no-yardopts")
24
+ end
25
+ assert out.empty?, "there should be no output"
26
+ assert err.empty?, "there should be no errors"
27
+ end
28
+
29
+ it "should run with a given path and .yardopts" do
30
+ out, err = capture_io do
31
+ # lib/something*.rb doesnot exist in the fixture, it is just given as
32
+ # a command-line path to check if it appears in the parsed filelist
33
+ @instance = @command.run("lib/something*.rb")
34
+ end
35
+ refute out.empty?, "there should be no output"
36
+ assert err.empty?, "there should be no errors"
37
+ end
38
+
39
+ it "should parse options with --no-yardopts" do
40
+ @options = ::Inch::CLI::Command::Options::List.new
41
+ @options.parse(["app/**/*.rb", "--no-yardopts"])
42
+ assert_equal ["app/**/*.rb"], @options.paths
43
+ end
44
+
45
+ it "should parse options with a given path and .yardopts" do
46
+ # lib/something*.rb doesnot exist in the fixture, it is just given as
47
+ # a command-line path to check if it appears in the parsed filelist
48
+ @options = ::Inch::CLI::Command::Options::List.new
49
+ @options.parse(["lib/something*.rb"])
50
+ assert_equal ["foo/**/*.rb", "lib/something*.rb"], @options.paths
51
+ end
52
+
53
+ it "should output info when run with --help" do
54
+ out, err = capture_io do
55
+ assert_raises(SystemExit) { @command.run("--help") }
56
+ end
57
+ refute out.empty?, "there should be some output"
58
+ assert_match /\bUsage\b.+list/, out
59
+ #assert_match /\b\-\-\[no\-\]yardopts\b/, out, "--[no-]yardopts should be mentioned"
60
+ assert err.empty?, "there should be no errors"
61
+ end
62
+ end
63
+
64
+ describe ::Inch::CLI::YardoptsHelper do
65
+ before do
66
+ Dir.chdir fixture_path(:simple)
67
+ refute File.file?(".yardopts")
68
+ refute File.file?(".document")
69
+ @command = ::Inch::CLI::Command::List
70
+ end
71
+
72
+ it "should not interfere with paths in arguments" do
73
+ @options = ::Inch::CLI::Command::Options::List.new
74
+ @options.parse(["lib/**/foo*.rb"])
75
+ assert_equal ["lib/**/foo*.rb"], @options.paths
76
+ end
77
+
78
+ it "should not intefer with --all at the end" do
79
+ @options = ::Inch::CLI::Command::Options::List.new
80
+ @options.parse(["lib/**/foo*.rb", "--all"])
81
+ assert_equal ["lib/**/foo*.rb"], @options.paths
82
+ end
83
+
84
+ end
@@ -0,0 +1,204 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
2
+
3
+ describe ::Inch::CodeObject::Docstring do
4
+
5
+
6
+ #
7
+ # loose TomDoc compatibility
8
+ #
9
+
10
+
11
+ it "should notice things in tomdoc style docs" do
12
+ text = <<-DOC
13
+ Public: Detects the Language of the blob.
14
+
15
+ param1 - String filename
16
+ param2 - String blob data. A block also maybe passed in for lazy
17
+ loading. This behavior is deprecated and you should always
18
+ pass in a String.
19
+ param3 - Optional String mode (defaults to nil)
20
+
21
+ Returns Language or nil.
22
+ DOC
23
+ docstring = ::Inch::CodeObject::Docstring.new(text)
24
+ assert docstring.mentions_parameter?(:param1)
25
+ assert docstring.mentions_parameter?(:param2)
26
+ assert docstring.mentions_parameter?(:param3)
27
+ assert docstring.describes_parameter?(:param1)
28
+ assert docstring.describes_parameter?(:param2)
29
+ assert docstring.describes_parameter?(:param3)
30
+ refute docstring.contains_code_example?
31
+ assert docstring.mentions_return?
32
+ end
33
+
34
+ it "should notice things in tomdoc style docs 2" do
35
+ text = <<-DOC
36
+ Public: Look up Language by one of its aliases.
37
+
38
+ param1 - A String alias of the Language
39
+
40
+ Examples
41
+
42
+ Language.find_by_alias('cpp')
43
+ # => #<Language name="C++">
44
+
45
+ Returns the Lexer or nil if none was found.
46
+ DOC
47
+ docstring = ::Inch::CodeObject::Docstring.new(text)
48
+ assert docstring.mentions_parameter?(:param1)
49
+ assert docstring.describes_parameter?(:param1)
50
+ refute docstring.mentions_parameter?(:alias)
51
+ refute docstring.mentions_parameter?(:Look)
52
+ assert docstring.contains_code_example?
53
+ assert docstring.mentions_return?
54
+ end
55
+
56
+ it "should notice things in tomdoc style docs 3" do
57
+ text = <<-DOC
58
+ Public: Look up Language by one of its aliases.
59
+
60
+ param1 - A String alias of the Language
61
+
62
+ Examples
63
+
64
+ Language.find_by_alias('cpp')
65
+ # => #<Language name="C++">
66
+
67
+ Returns the Lexer or nil if none was found.
68
+ DOC
69
+ docstring = ::Inch::CodeObject::Docstring.new(text)
70
+ assert docstring.mentions_parameter?(:param1)
71
+ assert docstring.describes_parameter?(:param1)
72
+ refute docstring.mentions_parameter?(:alias)
73
+ refute docstring.mentions_parameter?(:Look)
74
+ assert docstring.contains_code_example?
75
+ assert docstring.mentions_return?
76
+ end
77
+
78
+
79
+ #
80
+ # PARAMETER MENTIONS
81
+ #
82
+
83
+
84
+ it "should work 2" do
85
+ text = <<-DOC
86
+ Just because format_html is mentioned here, does not mean
87
+ the first parameter is mentioned.
88
+ DOC
89
+ docstring = ::Inch::CodeObject::Docstring.new(text)
90
+ refute docstring.mentions_parameter?(:format)
91
+ refute docstring.contains_code_example?
92
+ end
93
+
94
+
95
+ it "should work 2" do
96
+ text = <<-DOC
97
+ Just because format is mentioned here, does not mean
98
+ the first parameter is meant.
99
+ DOC
100
+ docstring = ::Inch::CodeObject::Docstring.new(text)
101
+ refute docstring.mentions_parameter?(:format)
102
+ refute docstring.contains_code_example?
103
+ end
104
+
105
+
106
+
107
+ #
108
+ # CODE EXAMPLES
109
+ #
110
+
111
+
112
+ it "should work 3" do
113
+ text = <<-DOC
114
+ An example of a method using RDoc rather than YARD.
115
+
116
+ == Parameters:
117
+ param1::
118
+ A Symbol declaring some markup language like `:md` or `:html`.
119
+
120
+ == Returns:
121
+ A string in the specified format.
122
+ DOC
123
+ docstring = ::Inch::CodeObject::Docstring.new(text)
124
+ refute docstring.contains_code_example?
125
+ end
126
+
127
+ it "should work with code example" do
128
+ text = <<-DOC
129
+ Another example.
130
+
131
+ method_with_code_example() # => some value
132
+
133
+ Params:
134
+ +param1+:: param1 line string to be executed by the system
135
+ +param2+:: +Proc+ object that takes a pipe object as first and only param (may be nil)
136
+ +param3+:: +Proc+ object that takes a pipe object as first and only param (may be nil)
137
+ DOC
138
+ docstring = ::Inch::CodeObject::Docstring.new(text)
139
+ assert docstring.contains_code_example?
140
+ assert docstring.mentions_parameter?(:param1)
141
+ assert docstring.mentions_parameter?(:param2)
142
+ assert docstring.mentions_parameter?(:param3)
143
+ assert docstring.describes_parameter?(:param1)
144
+ assert docstring.describes_parameter?(:param2)
145
+ assert docstring.describes_parameter?(:param3)
146
+ end
147
+
148
+ it "should work with code example 2" do
149
+ text = <<-DOC
150
+ Just because format_html is mentioned here, does not mean
151
+ the first parameter is mentioned.
152
+
153
+ method_with_code_example() # => some value
154
+ method_with_missing_param_doc(param1, param2, param3)
155
+ DOC
156
+ docstring = ::Inch::CodeObject::Docstring.new(text)
157
+ assert docstring.contains_code_example?
158
+ assert_equal 1, docstring.code_examples.size
159
+ end
160
+
161
+ it "should work with code example 3" do
162
+ text = <<-DOC
163
+ An example of a method using RDoc rather than YARD.
164
+
165
+ method_with_code_example() # => some value
166
+
167
+ == Parameters:
168
+ param1::
169
+ A Symbol declaring some markup language like `:md` or `:html`.
170
+
171
+ == Returns:
172
+ A string in the specified format.
173
+ DOC
174
+ docstring = ::Inch::CodeObject::Docstring.new(text)
175
+ assert docstring.contains_code_example?
176
+ assert_equal 1, docstring.code_examples.size
177
+ assert docstring.mentions_parameter?(:param1)
178
+ assert docstring.describes_parameter?(:param1)
179
+ end
180
+
181
+ it "should work with multiple code examples" do
182
+ text = <<-DOC
183
+ An example of a method using RDoc rather than YARD.
184
+
185
+ method_with_code_example() # => some value
186
+
187
+ Another example of a method:
188
+
189
+ Article.__elasticsearch__.create_index! 1, force: true
190
+ Article.__elasticsearch__.create_index! 2, force: true
191
+
192
+ == Parameters:
193
+ param1::
194
+ A Symbol declaring some markup language like `:md` or `:html`.
195
+
196
+ == Returns:
197
+ A string in the specified format.
198
+ DOC
199
+ docstring = ::Inch::CodeObject::Docstring.new(text)
200
+ assert docstring.contains_code_example?
201
+ assert_equal 2, docstring.code_examples.size
202
+ assert docstring.code_examples.last.index("create_index! 2")
203
+ end
204
+ end
@@ -0,0 +1,38 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
2
+
3
+ describe ::Inch::CodeObject::NodocHelper do
4
+ before do
5
+ Dir.chdir fixture_path(:simple)
6
+ @source_parser = Inch::SourceParser.run(["lib/**/*.rb"])
7
+ end
8
+
9
+ it "should return true for explicitly tagged objects" do
10
+ [
11
+ "Foo::Qux",
12
+ "Foo::Qux#method_with_implicit_nodoc",
13
+ "Foo::Qux::Quux#method_with_private_tag",
14
+ "Foo::Qux::Quux#method_with_explicit_nodoc",
15
+ "Foo::Qux::Quux::PRIVATE_VALUE",
16
+ "Foo::HiddenClass",
17
+ "Foo::HiddenClass::EvenMoreHiddenClass",
18
+ "Foo::HiddenClass::EvenMoreHiddenClass#method_with_implicit_nodoc",
19
+ ].each do |query|
20
+ m = @source_parser.find_object(query)
21
+ assert m.nodoc?, "nodoc? should return true for #{query}"
22
+ end
23
+ end
24
+
25
+ it "should return false for other objects" do
26
+ [
27
+ "Foo::Qux::Quux#method_without_nodoc",
28
+ "Foo::Qux::Quux::PUBLIC_VALUE",
29
+ "Foo::Qux::DOCCED_VALUE",
30
+ "Foo::HiddenClass::EvenMoreHiddenClass::SuddenlyVisibleClass",
31
+ "Foo::HiddenClass::EvenMoreHiddenClass::SuddenlyVisibleClass#method_with_implicit_doc",
32
+ ].each do |query|
33
+ m = @source_parser.find_object(query)
34
+ refute m.nodoc?, "nodoc? should return false for #{query}"
35
+ end
36
+ end
37
+
38
+ end