rails_best_practices 1.5.3 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/Gemfile.lock +1 -1
  2. data/README.md +11 -7
  3. data/assets/result.html.erb +6 -4
  4. data/lib/rails_best_practices/analyzer.rb +45 -42
  5. data/lib/rails_best_practices/command.rb +36 -9
  6. data/lib/rails_best_practices/core.rb +2 -0
  7. data/lib/rails_best_practices/core/check.rb +52 -18
  8. data/lib/rails_best_practices/core/error.rb +8 -0
  9. data/lib/rails_best_practices/core/helpers.rb +8 -0
  10. data/lib/rails_best_practices/core/modules.rb +39 -0
  11. data/lib/rails_best_practices/core/runner.rb +14 -3
  12. data/lib/rails_best_practices/prepares.rb +9 -0
  13. data/lib/rails_best_practices/prepares/controller_prepare.rb +15 -3
  14. data/lib/rails_best_practices/prepares/helper_prepare.rb +41 -0
  15. data/lib/rails_best_practices/prepares/mailer_prepare.rb +1 -1
  16. data/lib/rails_best_practices/prepares/model_prepare.rb +1 -1
  17. data/lib/rails_best_practices/reviews.rb +1 -0
  18. data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +2 -2
  19. data/lib/rails_best_practices/reviews/remove_unused_methods_in_controllers_review.rb +13 -3
  20. data/lib/rails_best_practices/reviews/remove_unused_methods_in_helpers_review.rb +47 -0
  21. data/lib/rails_best_practices/reviews/remove_unused_methods_in_models_review.rb +3 -3
  22. data/lib/rails_best_practices/version.rb +1 -1
  23. data/rails_best_practices.yml +1 -0
  24. data/spec/rails_best_practices/analyzer_spec.rb +3 -3
  25. data/spec/rails_best_practices/core/error_spec.rb +10 -1
  26. data/spec/rails_best_practices/core/helpers_spec.rb +5 -0
  27. data/spec/rails_best_practices/core/modules_spec.rb +26 -0
  28. data/spec/rails_best_practices/prepares/controller_prepare_spec.rb +32 -1
  29. data/spec/rails_best_practices/prepares/helper_prepare_spec.rb +41 -0
  30. data/spec/rails_best_practices/reviews/always_add_db_index_review_spec.rb +11 -11
  31. data/spec/rails_best_practices/reviews/remove_unused_methods_in_controllers_review_spec.rb +15 -15
  32. data/spec/rails_best_practices/reviews/remove_unused_methods_in_helpers_review_spec.rb +84 -0
  33. data/spec/rails_best_practices/reviews/remove_unused_methods_in_models_review_spec.rb +31 -31
  34. metadata +35 -23
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rails_best_practices (1.5.3)
4
+ rails_best_practices (1.6.0)
5
5
  activesupport
6
6
  colored
7
7
  erubis
data/README.md CHANGED
@@ -22,22 +22,24 @@ By default rails_best_practices will do parse codes in vendor, spec, test and fe
22
22
 
23
23
  $ rails_best_practices -h
24
24
  Usage: rails_best_practices [options]
25
- -d, --debug Debug mode
25
+ -d, --debug debug mode
26
+ --silent silent mode
26
27
  -f, --format FORMAT output format
27
28
  --without-color only output plain text without color
28
29
  --with-textmate open file by textmate in html format
29
30
  --with-mvim open file by mvim in html format
31
+ --with-github open file on github in html format
30
32
  --with-git display git commit and git username in html format
31
33
  --with-hg display hg commit and hg username in html format
32
34
  --vendor include vendor files
33
35
  --spec include spec files
34
36
  --test include test files
35
37
  --features include features files
36
- -x, --exclude PATTERNS Don't analyze files matching a pattern
38
+ -x, --exclude PATTERNS don't analyze files matching a pattern
37
39
  (comma-separated regexp list)
38
- -g, --generate Generate configuration yaml
39
- -v, --version Show this version
40
- -h, --help Show this message
40
+ -g, --generate generate configuration yaml
41
+ -v, --version show this version
42
+ -h, --help show this message
41
43
 
42
44
  Resources
43
45
  ---------
@@ -112,7 +114,7 @@ Now you can customize this configuration file, the default configuration is as f
112
114
  UseObserverCheck: { }
113
115
  IsolateSeedDataCheck: { }
114
116
  AlwaysAddDbIndexCheck: { }
115
- UseBeforeFilterCheck: { customize_count: 1 }
117
+ UseBeforeFilterCheck: { customize_count: 2 }
116
118
  MoveCodeIntoControllerCheck: { }
117
119
  MoveCodeIntoModelCheck: { use_count: 2 }
118
120
  MoveCodeIntoHelperCheck: { array_count: 3 }
@@ -129,6 +131,7 @@ Now you can customize this configuration file, the default configuration is as f
129
131
  RestrictAutoGeneratedRoutesCheck: { }
130
132
  RemoveUnusedMethodsInModelsCheck: { except_methods: [] }
131
133
  RemoveUnusedMethodsInControllersCheck: { except_methods: [] }
134
+ RemoveUnusedMethodsInHelpersCheck: { except_methods: [] }
132
135
 
133
136
  You can remove or comment one review to disable it, and you can change the options.
134
137
 
@@ -173,11 +176,12 @@ Controller
173
176
 
174
177
  1. Use before_filter
175
178
  2. Simplify render in controllers
176
- 3. Remove unused methods In controllers
179
+ 3. Remove unused methods in controllers
177
180
 
178
181
  Helper
179
182
 
180
183
  1. Remove empty helpers
184
+ 2. Remove unused methods in helpers
181
185
 
182
186
  View
183
187
 
@@ -84,12 +84,14 @@
84
84
  <% @errors.each do |error| %>
85
85
  <tr class="<%= error.type.split(':').last %>">
86
86
  <td class='filename'>
87
- <% if @textmate %>
88
- <a href='txmt://open/?url=file://<%= File.expand_path(error.filename) %>&amp;line=<%= error.line_number %>'><%= error.filename %></a>
87
+ <% if @github %>
88
+ <a href='https://github.com/<%= @github_name %>/blob/<%= @last_commit_id %>/<%= error.short_filename %>#L<%= error.first_line_number %>' target='_blank'><%= error.short_filename %></a>
89
+ <% elsif @textmate %>
90
+ <a href='txmt://open/?url=file://<%= File.expand_path(error.filename) %>&amp;line=<%= error.line_number %>'><%= error.short_filename %></a>
89
91
  <% elsif @mvim %>
90
- <a href='mvim://open/?url=file://<%= File.expand_path(error.filename) %>&amp;line=<%= error.line_number %>'><%= error.filename %></a>
92
+ <a href='mvim://open/?url=file://<%= File.expand_path(error.filename) %>&amp;line=<%= error.line_number %>'><%= error.short_filename %></a>
91
93
  <% else %>
92
- <%= error.filename %>
94
+ <%= error.short_filename %>
93
95
  <% end %>
94
96
  </td>
95
97
  <td class='line'><%= error.line_number %></td>
@@ -45,22 +45,26 @@ module RailsBestPractices
45
45
  # @param [String] path the directory of rails project
46
46
  # @param [Hash] options
47
47
  def analyze
48
- @options[:exclude] ||= []
48
+ @options["exclude"] ||= []
49
+ @options["output-file"] ||= "rails_best_practices_output.html"
49
50
 
50
51
  Core::Runner.base_path = @path
51
52
  @runner = Core::Runner.new
52
- @runner.debug = true if @options['debug']
53
- @runner.color = !@options['without-color']
53
+ @runner.debug = true if @options["debug"]
54
+ @runner.color = !@options["without-color"]
54
55
 
55
- @bar = ProgressBar.new('Source Codes', parse_files.size * 3)
56
+ @bar = ProgressBar.new('Source Codes', parse_files.size * 3) if display_bar?
56
57
  ["lexical", "prepare", "review"].each { |process| send(:process, process) }
57
- @runner.on_complete
58
- @bar.finish
58
+ @bar.finish if display_bar?
59
+ end
60
+
61
+ def display_bar?
62
+ !@options["debug"] && !@options["silent"]
59
63
  end
60
64
 
61
65
  # Output the analyze result.
62
66
  def output
63
- if @options['format'] == 'html'
67
+ if @options["format"] == 'html'
64
68
  if @options["with-hg"]
65
69
  load_hg_info
66
70
  elsif @options["with-git"]
@@ -81,8 +85,9 @@ module RailsBestPractices
81
85
  def process(process)
82
86
  parse_files.each do |file|
83
87
  @runner.send("#{process}_file", file)
84
- @bar.inc unless @options['debug']
88
+ @bar.inc if display_bar?
85
89
  end
90
+ @runner.send("after_#{process}")
86
91
  end
87
92
 
88
93
  # get all files for parsing.
@@ -94,12 +99,12 @@ module RailsBestPractices
94
99
  files = file_sort(files)
95
100
 
96
101
  # By default, tmp, vender, spec, test, features are ignored.
97
- ['vendor', 'spec', 'test', 'features', 'tmp'].each do |pattern|
102
+ ["vendor", "spec", "test", "features", "tmp"].each do |pattern|
98
103
  files = file_ignore(files, "#{pattern}/") unless @options[pattern]
99
104
  end
100
105
 
101
106
  # Exclude files based on exclude regexes if the option is set.
102
- @options[:exclude].each do |pattern|
107
+ @options["exclude"].each do |pattern|
103
108
  files = file_ignore(files, pattern)
104
109
  end
105
110
 
@@ -125,37 +130,22 @@ module RailsBestPractices
125
130
  end
126
131
 
127
132
 
128
- # sort files, models first, then mailers, and sort other files by characters.
133
+ # sort files, models first, mailers, helpers, and then sort other files by characters.
129
134
  #
130
135
  # models and mailers first as for prepare process.
131
136
  #
132
137
  # @param [Array] files
133
138
  # @return [Array] sorted files
134
139
  def file_sort files
135
- models = []
136
- mailers = []
137
- files.each do |a|
138
- if a =~ Core::Check::MODEL_FILES
139
- models << a
140
- end
141
- end
142
- files.each do |a|
143
- if a =~ Core::Check::MAILER_FILES
144
- mailers << a
145
- end
146
- end
147
- files.collect! do |a|
148
- if a =~ Core::Check::MAILER_FILES || a =~ Core::Check::MODEL_FILES
149
- #nil
150
- else
151
- a
152
- end
153
- end
154
- files.compact!
140
+ models = files.find_all { |file| file =~ Core::Check::MODEL_FILES }
141
+ mailers = files.find_all { |file| file =~ Core::Check::MAILER_FILES }
142
+ helpers = files.find_all { |file| file =~ Core::Check::HELPER_FILES }
143
+ others = files.find_all { |file| file !~ Core::Check::MAILER_FILES && file !~ Core::Check::MODEL_FILES && file !~ Core::Check::HELPER_FILES }
155
144
  models.sort
156
145
  mailers.sort
157
- files.sort
158
- return models + mailers + files
146
+ helpers.sort
147
+ others.sort
148
+ return models + mailers + helpers + others
159
149
  end
160
150
 
161
151
  # ignore specific files.
@@ -180,7 +170,7 @@ module RailsBestPractices
180
170
 
181
171
  # load hg commit and hg username info.
182
172
  def load_hg_info
183
- hg_progressbar = ProgressBar.new('Hg Info', @runner.errors.size)
173
+ hg_progressbar = ProgressBar.new('Hg Info', @runner.errors.size) if display_bar?
184
174
  @runner.errors.each do |error|
185
175
  hg_info = `cd #{@runner.class.base_path}; hg blame -lvcu #{error.filename[@runner.class.base_path.size..-1].gsub(/^\//, "")} | sed -n /:#{error.line_number.split(',').first}:/p`
186
176
  unless hg_info == ""
@@ -188,14 +178,14 @@ module RailsBestPractices
188
178
  error.hg_username = hg_commit_username.split(/\ /)[0..-2].join(' ')
189
179
  error.hg_commit = hg_commit_username.split(/\ /)[-1]
190
180
  end
191
- hg_progressbar.inc unless @options['debug']
181
+ hg_progressbar.inc if display_bar?
192
182
  end
193
- hg_progressbar.finish
183
+ hg_progressbar.finish if display_bar?
194
184
  end
195
185
 
196
186
  # load git commit and git username info.
197
187
  def load_git_info
198
- git_progressbar = ProgressBar.new('Git Info', @runner.errors.size)
188
+ git_progressbar = ProgressBar.new('Git Info', @runner.errors.size) if display_bar?
199
189
  @runner.errors.each do |error|
200
190
  git_info = `cd #{@runner.class.base_path}; git blame #{error.filename[@runner.class.base_path.size..-1]} | sed -n #{error.line_number.split(',').first}p`
201
191
  unless git_info == ""
@@ -203,19 +193,32 @@ module RailsBestPractices
203
193
  error.git_commit = git_commit.split(" ").first.strip
204
194
  error.git_username = git_username.strip
205
195
  end
206
- git_progressbar.inc unless @options['debug']
196
+ git_progressbar.inc if display_bar?
207
197
  end
208
- git_progressbar.finish
198
+ git_progressbar.finish if display_bar?
209
199
  end
210
200
 
211
201
  # output errors with html format.
212
202
  def output_html_errors
213
203
  require 'erubis'
214
- template = File.read(File.join(File.dirname(__FILE__), "..", "..", "assets", "result.html.erb"))
204
+ template = @options["template"] ? File.read(File.expand_path(@options["template"])) : File.read(File.join(File.dirname(__FILE__), "..", "..", "assets", "result.html.erb"))
215
205
 
216
- File.open("rails_best_practices_output.html", "w+") do |file|
206
+ if @options["with-github"]
207
+ last_commit_id = @options["last-commit-id"] ? @options["last-commit-id"] : `cd #{@runner.class.base_path}; git rev-parse HEAD`.chomp
208
+ end
209
+ File.open(@options["output-file"], "w+") do |file|
217
210
  eruby = Erubis::Eruby.new(template)
218
- file.puts eruby.evaluate(:errors => @runner.errors, :error_types => error_types, :textmate => @options["with-textmate"], :mvim => @options["with-mvim"], :git => @options["with-git"], :hg => @options["with-hg"])
211
+ file.puts eruby.evaluate(
212
+ :errors => @runner.errors,
213
+ :error_types => error_types,
214
+ :textmate => @options["with-textmate"],
215
+ :mvim => @options["with-mvim"],
216
+ :github => @options["with-github"],
217
+ :github_name => @options["github-name"],
218
+ :last_commit_id => last_commit_id,
219
+ :git => @options["with-git"],
220
+ :hg => @options["with-hg"]
221
+ )
219
222
  end
220
223
  end
221
224
 
@@ -2,20 +2,26 @@
2
2
  require 'optparse'
3
3
 
4
4
  # Usage: rails_best_practices [options] path
5
- # -d, --debug Debug mode
5
+ # -d, --debug debug mode
6
+ # --silent silent mode
6
7
  # -f, --format FORMAT output format
8
+ # --output-file FILE output html file for the analyzing result
7
9
  # --without-color only output plain text without color
8
10
  # --with-textmate open file by textmate in html format
9
11
  # --with-mvim open file by mvim in html format
12
+ # --with-github GITHUB_NAME open file on github in html format, GITHUB_NAME is like flyerhzm/rails-bestpractices.com
13
+ # --with-git display git commit and username, only support html format
14
+ # --with-hg display hg commit and username, only support html format
15
+ # --template TEMPLATE customize erb template
10
16
  # --vendor include vendor files
11
17
  # --spec include spec files
12
18
  # --test include test files
13
19
  # --features include features files
14
- # -x, --exclude PATTERNS Don't analyze files matching a pattern
20
+ # -x, --exclude PATTERNS don't analyze files matching a pattern
15
21
  # (comma-separated regexp list)
16
- # -g, --generate Generate configuration yaml
17
- # -v, --version Show this version
18
- # -h, --help Show this message
22
+ # -g, --generate generate configuration yaml
23
+ # -v, --version show this version
24
+ # -h, --help show this message
19
25
  options = {}
20
26
  OptionParser.new do |opts|
21
27
  opts.banner = "Usage: rails_best_practices [options] path"
@@ -40,6 +46,15 @@ OptionParser.new do |opts|
40
46
  options["with-mvim"] = true
41
47
  end
42
48
 
49
+ opts.on("--with-github GITHUB_NAME", "open file on github in html format") do |github_name|
50
+ options["with-github"] = true
51
+ options["github-name"] = github_name
52
+ end
53
+
54
+ opts.on("--last-commit-id COMMIT_ID", "last commit id") do |commit_id|
55
+ options["last-commit-id"] = commit_id
56
+ end
57
+
43
58
  opts.on("--with-hg", "display hg commit and username, only support html format") do
44
59
  options["with-hg"] = true
45
60
  end
@@ -48,7 +63,19 @@ OptionParser.new do |opts|
48
63
  options["with-git"] = true
49
64
  end
50
65
 
51
- ['vendor', 'spec', 'test', 'features'].each do |pattern|
66
+ opts.on("--template TEMPLATE", "customize erb template") do |template|
67
+ options["template"] = template
68
+ end
69
+
70
+ opts.on("--output-file OUTPUT_FILE", "output html file for the analyzing result") do |output_file|
71
+ options["output-file"] = output_file
72
+ end
73
+
74
+ opts.on("--silent", "silent mode") do
75
+ options["silent"] = true
76
+ end
77
+
78
+ ["vendor", "spec", "test", "features"].each do |pattern|
52
79
  opts.on("--#{pattern}", "include #{pattern} files") do
53
80
  options[pattern] = true
54
81
  end
@@ -67,20 +94,20 @@ OptionParser.new do |opts|
67
94
 
68
95
  opts.on("-x", "--exclude PATTERNS", "Don't analyze files matching a pattern", "(comma-separated regexp list)") do |list|
69
96
  begin
70
- options[:exclude] = list.split(/,/).map{|x| Regexp.new x}
97
+ options["exclude"] = list.split(/,/).map{|x| Regexp.new x}
71
98
  rescue RegexpError => e
72
99
  raise OptionParser::InvalidArgument, e.message
73
100
  end
74
101
  end
75
102
 
76
103
  opts.on("-g", "--generate", "Generate configuration yaml") do
77
- options[:generate] = true
104
+ options["generate"] = true
78
105
  end
79
106
 
80
107
  opts.parse!
81
108
  end
82
109
 
83
- if options[:generate]
110
+ if options["generate"]
84
111
  RailsBestPractices::Analyzer.new(ARGV.first).generate
85
112
  else
86
113
  analyzer = RailsBestPractices::Analyzer.new(ARGV.first, options)
@@ -5,12 +5,14 @@ require 'rails_best_practices/core/checking_visitor'
5
5
  require 'rails_best_practices/core/error'
6
6
  require 'rails_best_practices/core/nil'
7
7
  require 'rails_best_practices/core/klasses'
8
+ require 'rails_best_practices/core/modules'
8
9
  require 'rails_best_practices/core/models'
9
10
  require 'rails_best_practices/core/model_associations'
10
11
  require 'rails_best_practices/core/model_attributes'
11
12
  require 'rails_best_practices/core/mailers'
12
13
  require 'rails_best_practices/core/methods'
13
14
  require 'rails_best_practices/core/controllers'
15
+ require 'rails_best_practices/core/helpers'
14
16
  require 'rails_best_practices/core/routes'
15
17
 
16
18
  require 'rails_best_practices/core_ext/sexp'
@@ -8,8 +8,8 @@ module RailsBestPractices
8
8
  MIGRATION_FILES = /db\/migrate\/.*\.rb$/
9
9
  MODEL_FILES = /models\/.*\.rb$/
10
10
  MAILER_FILES = /models\/.*mailer\.rb$|mailers\/.*mailer\.rb/
11
- VIEW_FILES = /(views|cells)\/.*\.(erb|haml)$/
12
- PARTIAL_VIEW_FILES = /(views|cells)\/.*\/_.*\.(erb|haml)$/
11
+ VIEW_FILES = /(views|cells)\/.*\.(erb|haml|builder)$/
12
+ PARTIAL_VIEW_FILES = /(views|cells)\/.*\/_.*\.(erb|haml|builder)$/
13
13
  ROUTE_FILES = /config\/routes.*\.rb/
14
14
  SCHEMA_FILE = /db\/schema\.rb/
15
15
  HELPER_FILES = /helpers\/.*\.rb$/
@@ -67,6 +67,9 @@ module RailsBestPractices
67
67
  end
68
68
  end
69
69
 
70
+ def after_prepare; end
71
+ def after_review; end
72
+
70
73
  # add error if source code violates rails best practice.
71
74
  #
72
75
  # @param [String] message, is the string message for violation of the rails best practice
@@ -135,24 +138,24 @@ module RailsBestPractices
135
138
  end
136
139
 
137
140
  # Helper to parse the class name.
138
- module Klassable
141
+ module Classable
139
142
  def self.included(base)
140
143
  base.class_eval do
141
144
  interesting_nodes :module, :class
142
145
 
143
146
  # remember module name
144
147
  add_callback "start_module" do |node|
145
- modules << node.module_name.to_s
148
+ classable_modules << node.module_name.to_s
146
149
  end
147
150
 
148
151
  # end of the module.
149
152
  add_callback "end_module" do |node|
150
- modules.pop
153
+ classable_modules.pop
151
154
  end
152
155
 
153
156
  # remember the class anem
154
157
  add_callback "start_class" do |node|
155
- @klass = Core::Klass.new(node.class_name.to_s, node.base_class.to_s, modules)
158
+ @klass = Core::Klass.new(node.class_name.to_s, node.base_class.to_s, classable_modules)
156
159
  end
157
160
 
158
161
  # end of the class
@@ -172,27 +175,53 @@ module RailsBestPractices
172
175
  @klass.extend_class_name
173
176
  end
174
177
 
178
+ # modules.
179
+ def classable_modules
180
+ @class_moduels ||= []
181
+ end
182
+ end
183
+
184
+ # Helper to parse the module name.
185
+ module Moduleable
186
+ def self.included(base)
187
+ base.class_eval do
188
+ interesting_nodes :module
189
+
190
+ # remember module name
191
+ add_callback "start_module" do |node|
192
+ moduleable_modules << node.module_name.to_s
193
+ end
194
+
195
+ # end of module
196
+ add_callback "end_module" do |node|
197
+ moduleable_modules.pop
198
+ end
199
+ end
200
+ end
201
+
175
202
  # get the current module name.
176
203
  def current_module_name
177
- modules.join("::")
204
+ moduleable_modules.join("::")
178
205
  end
179
206
 
180
207
  # modules.
181
- def modules
182
- @moduels ||= []
208
+ def moduleable_modules
209
+ @moduleable_moduels ||= []
183
210
  end
184
211
  end
185
212
 
186
213
  # Helper to add callback after all files reviewed.
187
- module Completeable
214
+ module Afterable
188
215
  def self.included(base)
189
216
  base.class_eval do
190
217
  interesting_nodes :class
191
- interesting_files /rails_best_practices\.complete/
218
+ interesting_files /rails_best_practices\.after_(prepare|review)/
192
219
 
193
220
  add_callback "end_class" do |node|
194
- if "RailsBestPractices::Complete" == node.class_name.to_s
195
- on_complete
221
+ if "RailsBestPractices::AfterPrepare" == node.class_name.to_s
222
+ after_prepare
223
+ elsif "RailsBestPractices::AfterReview" == node.class_name.to_s
224
+ after_review
196
225
  end
197
226
  end
198
227
  end
@@ -294,12 +323,13 @@ module RailsBestPractices
294
323
  call_method(method_name)
295
324
  end
296
325
 
297
- def call_method(method_name, class_name=current_class_name)
298
- if methods.has_method?(class_name, method_name)
299
- methods.get_method(class_name, method_name).mark_used
326
+ def call_method(method_name, class_name=nil)
327
+ name ||= respond_to?(:current_class_name) ? current_class_name : current_module_name
328
+ if methods.has_method?(name, method_name)
329
+ methods.get_method(name, method_name).mark_used
300
330
  end
301
- methods.mark_parent_class_method_used(class_name, method_name)
302
- methods.mark_subclasses_method_used(class_name, method_name)
331
+ methods.mark_parent_class_method_used(name, method_name)
332
+ methods.mark_subclasses_method_used(name, method_name)
303
333
  methods.possible_public_used(method_name)
304
334
  end
305
335
  end
@@ -347,6 +377,10 @@ module RailsBestPractices
347
377
  (class_name == method.class_name && method_name == method.method_name)
348
378
  end
349
379
  end
380
+
381
+ def internal_except_methods
382
+ raise NoMethodError.new "no method internal_except_methods"
383
+ end
350
384
  end
351
385
  end
352
386
  end