maximus 0.1.5.1 → 0.1.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,6 +17,13 @@ module Maximus
17
17
  defined?(Rails)
18
18
  end
19
19
 
20
+ # See if project is a Middleman app
21
+ # @since 0.1.7
22
+ # @return [Boolean]
23
+ def is_middleman?
24
+ Gem::Specification::find_all_by_name('middleman').any?
25
+ end
26
+
20
27
  # Get root directory of file being called
21
28
  # @return [String] absolute path to root directory
22
29
  def root_dir
@@ -37,20 +44,11 @@ module Maximus
37
44
  end
38
45
  end
39
46
 
40
- # Look for a file in the config directory
41
- #
42
- # @since 0.1.0
43
- # @param file [String] filename with extension to search for
44
- # @return [String] path to default config file or file in user's directory
45
- def check_default_config_path(file)
46
- File.exist?(file) ? file : File.join(File.dirname(__FILE__), file)
47
- end
48
-
49
47
  # Grab the absolute path of the reporter file
50
48
  # @param filename [String]
51
49
  # @return [String] absolute path to the reporter file
52
50
  def reporter_path(filename)
53
- File.join(File.dirname(__FILE__), "reporter/#{filename}")
51
+ File.join(File.dirname(__FILE__), 'reporter', filename)
54
52
  end
55
53
 
56
54
  # Find all files that were linted by extension
@@ -99,25 +97,6 @@ module Maximus
99
97
  STDIN.gets
100
98
  end
101
99
 
102
- # Convert the array from lines_added into spelled-out ranges
103
- # This is a GitControl helper but it's used in Lint
104
- # @see GitControl#lines_added
105
- # @see Lint#relevant_lint
106
- #
107
- # @example typical output
108
- # lines_added = {changes: ['0..10', '11..14']}
109
- # lines_added_to_range(lines_added)
110
- # # output
111
- # [0,1,2,3,4,5,6,7,8,9,10, 11,12,13,14]
112
- #
113
- # @todo I'm sure there's a better way of doing this
114
- # @todo figure out a better place to put this than in Helper
115
- # @return [Hash] changes_array of spelled-out arrays of integers
116
- def lines_added_to_range(file)
117
- changes_array = file[:changes].map { |ch| ch.split("..").map(&:to_i) }
118
- changes_array.map { |e| (e[0]..e[1]).to_a }.flatten!
119
- end
120
-
121
100
  # Ensure path exists
122
101
  # @param path [String, Array] path to files can be directory or glob
123
102
  # @return [Boolean]
@@ -131,6 +110,7 @@ module Maximus
131
110
  end
132
111
  end
133
112
  else
113
+ path = path.gsub('/**', '').gsub('/*', '').split('.')[0..1].first if path.include?('*')
134
114
  if File.exist?(path)
135
115
  return true
136
116
  else
@@ -140,5 +120,22 @@ module Maximus
140
120
  end
141
121
  end
142
122
 
123
+ # Default paths to check for lints and some stats
124
+ # @since 0.1.7
125
+ # @param root [String] base directory
126
+ # @param folder [String] nested folder to search for for Rails or Middleman
127
+ # @param extension [String] file glob type to search for if neither
128
+ # @return [String] path to desired files
129
+ def discover_path(root = @config.working_dir, folder = '', extension = '')
130
+ return @path unless @path.blank?
131
+ if is_rails?
132
+ File.join(root, 'app', 'assets', folder)
133
+ elsif is_middleman?
134
+ File.join(root, 'source', folder)
135
+ else
136
+ extension.blank? ? File.join(root) : File.join(root, '/**', "/*.#{extension}")
137
+ end
138
+ end
139
+
143
140
  end
144
141
  end
@@ -1,4 +1,5 @@
1
1
  require 'json'
2
+ require 'rainbow'
2
3
 
3
4
  module Maximus
4
5
 
@@ -48,27 +49,18 @@ module Maximus
48
49
  # @param data [Hash] unfiltered lint data
49
50
  # @return [Hash] refined lint data and all the other bells and whistles
50
51
  def refine(data)
52
+ @task ||= ''
51
53
 
52
- # Prevent abortive empty JSON.parse error
53
- data = '{}' if data.blank?
54
- return puts "Error from #{@task}: #{data}" if data.is_a?(String) && data.include?('No such')
55
-
56
- data = data.is_a?(String) ? JSON.parse(data) : data
57
-
58
- @output[:relevant_lints] = relevant_lints( data, @git_files ) unless @git_files.blank?
59
- unless @settings[:commit].blank?
60
- data = @output[:relevant_lints]
61
- end
54
+ data = parse_data(data)
55
+ return puts data if data.is_a?(String)
62
56
 
63
57
  evaluate_severities(data)
64
58
 
65
- lint_count = (@output[:lint_errors].length + @output[:lint_warnings].length + @output[:lint_conventions].length + @output[:lint_refactors].length)
66
-
67
59
  puts lint_summarize
68
60
 
69
61
  if @config.is_dev?
70
- puts lint_dev_format(data) unless data.blank?
71
- lint_ceiling lint_count
62
+ puts lint_dev_format(data)
63
+ lint_ceiling
72
64
  else
73
65
  # Because this should be returned in the format it was received
74
66
  @output[:raw_data] = data.to_json
@@ -84,7 +76,7 @@ module Maximus
84
76
  # @param delimiter [String] comma or space separated
85
77
  # @param remove [String] remove from all file names
86
78
  # @return all_files [Array<string>] list of file names
87
- def files_inspected(ext, delimiter = ',', remove = @settings[:root_dir])
79
+ def files_inspected(ext, delimiter = ',', remove = @config.working_dir)
88
80
  @path.is_a?(Array) ? @path.split(delimiter) : file_list(@path, ext, remove)
89
81
  end
90
82
 
@@ -98,24 +90,26 @@ module Maximus
98
90
 
99
91
  # sometimes data will be blank but this is good - it means no errors were raised in the lint
100
92
  next if lint.blank?
101
- lint_file = lint[file[:filename].to_s]
93
+ lint_file = lint[file[:filename]]
94
+
95
+ next if lint_file.blank?
102
96
 
103
97
  expanded = lines_added_to_range(file)
104
- revert_name = file[:filename].gsub("#{@settings[:root_dir]}/", '')
105
- unless lint_file.blank?
106
- all_files[revert_name] = []
107
-
108
- # @todo originally I tried .map and delete_if, but this works,
109
- # and the other method didn't cover all bases.
110
- # Gotta be a better way to write this though
111
- lint_file.each do |l|
112
- if expanded.include?(l['line'].to_i)
113
- all_files[revert_name] << l
114
- end
98
+ revert_name = strip_working_dir(file[:filename])
99
+
100
+ all_files[revert_name] = []
101
+
102
+ # @todo originally I tried .map and delete_if, but this works,
103
+ # and the other method didn't cover all bases.
104
+ # Gotta be a better way to write this though
105
+ lint_file.each do |l|
106
+ if expanded.include?(l['line'].to_i)
107
+ all_files[revert_name] << l
115
108
  end
116
- # If there's nothing there, then it definitely isn't a relevant lint
117
- all_files.delete(revert_name) if all_files[revert_name].blank?
118
109
  end
110
+
111
+ # If there's nothing there, then it definitely isn't a relevant lint
112
+ all_files.delete(revert_name) if all_files[revert_name].blank?
119
113
  end
120
114
  @output[:files_linted] = all_files.keys
121
115
  all_files
@@ -138,36 +132,56 @@ module Maximus
138
132
  @output[:lint_errors] = []
139
133
  @output[:lint_conventions] = []
140
134
  @output[:lint_refactors] = []
135
+ @output[:lint_fatals] = []
136
+
141
137
  return if data.blank?
138
+
142
139
  data.each do |filename, error_list|
143
140
  error_list.each do |message|
144
141
  # so that :raw_data remains unaffected
145
142
  message = message.clone
146
143
  message.delete('length')
147
- message['filename'] = filename.nil? ? '' : filename.gsub("#{@settings[:root_dir]}/", '')
148
- severity = message['severity']
144
+ message['filename'] = filename.nil? ? '' : strip_working_dir(filename)
145
+ severity = "lint_#{message['severity']}s".to_sym
149
146
  message.delete('severity')
150
- @output["lint_#{severity}s".to_sym] << message
147
+ @output[severity] << message if @output.key?(severity)
151
148
  end
152
149
  end
153
- return @output
150
+ @output
154
151
  end
155
152
 
153
+ # Convert the array from lines_added into spelled-out ranges
154
+ # This is a GitControl helper but it's used in Lint
155
+ # @see GitControl#lines_added
156
+ # @see Lint#relevant_lint
157
+ #
158
+ # @example typical output
159
+ # lines_added = {changes: ['0..10', '11..14']}
160
+ # lines_added_to_range(lines_added)
161
+ # # output
162
+ # [0,1,2,3,4,5,6,7,8,9,10, 11,12,13,14]
163
+ #
164
+ # @return [Hash] changes_array of spelled-out arrays of integers
165
+ def lines_added_to_range(file)
166
+ changes_array = file[:changes].map { |ch| ch.split("..").map(&:to_i) }
167
+ changes_array.map { |e| (e[0]..e[1]).to_a }.flatten!
168
+ end
156
169
 
157
170
  private
158
171
 
159
172
  # Send abbreviated results to console or to the log
160
173
  # @return [String] console message to display
161
174
  def lint_summarize
162
- puts "#{'Warning'.color(:red)}: #{@output[:lint_errors].length} errors found in #{@task}" unless @output[:lint_errors].length
175
+ puts "#{'Warning'.color(:red)}: #{@output[:lint_errors].length} errors found in #{@task}" if @output[:lint_errors].length > 0
163
176
 
164
- success = @task.to_s.color(:green)
165
- success += ": "
166
- success += "[#{@output[:lint_warnings].length}]".color(:yellow)
167
- success += " [#{@output[:lint_errors].length}]".color(:red)
177
+ success = @task.color(:green)
178
+ success << ": "
179
+ success << "[#{@output[:lint_warnings].length}]".color(:yellow)
180
+ success << " [#{@output[:lint_errors].length}]".color(:red)
168
181
  if @task == 'rubocop'
169
- success += " [#{@output[:lint_conventions].length}]".color(:cyan)
170
- success += " [#{@output[:lint_refactors].length}]".color(:white)
182
+ success << " [#{@output[:lint_conventions].length}]".color(:cyan)
183
+ success << " [#{@output[:lint_refactors].length}]".color(:white)
184
+ success << " [#{@output[:lint_fatals].length}]".color(:magenta)
171
185
  end
172
186
 
173
187
  success
@@ -176,12 +190,22 @@ module Maximus
176
190
  # If there's just too much to handle, through a warning.
177
191
  # @param lint_length [Integer] count of how many lints
178
192
  # @return [String] console message to display
179
- def lint_ceiling(lint_length)
193
+ def lint_ceiling
194
+ lint_length = (@output[:lint_errors].length + @output[:lint_warnings].length + @output[:lint_conventions].length + @output[:lint_refactors].length + @output[:lint_fatals].length)
195
+
180
196
  return unless lint_length > 100
181
197
  failed_task = @task.color(:green)
182
198
  errors = "#{lint_length} failures.".color(:red)
183
- errormsg = ["You wouldn't stand a chance in Rome.\nResolve thy errors and train with #{failed_task} again.", "The gods frown upon you, mortal.\n#{failed_task}. Again.", "Do not embarrass the city. Fight another day. Use #{failed_task}.", "You are without honor. Replenish it with another #{failed_task}.", "You will never claim the throne with a performance like that.", "Pompeii has been lost.", "A wise choice. Do not be discouraged from another #{failed_task}."].sample
184
- errormsg += "\n\n"
199
+ errormsg = [
200
+ "You wouldn't stand a chance in Rome.\nResolve thy errors and train with #{failed_task} again.",
201
+ "The gods frown upon you, mortal.\n#{failed_task}. Again.",
202
+ "Do not embarrass the city. Fight another day. Use #{failed_task}.",
203
+ "You are without honor. Replenish it with another #{failed_task}.",
204
+ "You will never claim the throne with a performance like that.",
205
+ "Pompeii has been lost.",
206
+ "A wise choice. Do not be discouraged from another #{failed_task}."
207
+ ].sample
208
+ errormsg << "\n\n"
185
209
 
186
210
  go_on = prompt "\n#{errors} Continue? (y/n) "
187
211
  abort errormsg unless truthy?(go_on)
@@ -194,23 +218,48 @@ module Maximus
194
218
  return if errors.blank?
195
219
  pretty_output = ''
196
220
  errors.each do |filename, error_list|
197
- filename = filename.gsub("#{@settings[:root_dir]}/", '')
198
- pretty_output += "\n#{filename.color(:cyan).underline} \n"
221
+ filename = strip_working_dir(filename)
222
+ pretty_output << "\n#{filename.color(:cyan).underline} \n"
199
223
  error_list.each do |message|
200
- pretty_output += case message['severity']
224
+ pretty_output << case message['severity']
201
225
  when 'warning' then 'W'.color(:yellow)
202
226
  when 'error' then 'E'.color(:red)
203
227
  when 'convention' then 'C'.color(:cyan)
204
228
  when 'refactor' then 'R'.color(:white)
229
+ when 'fatal' then 'F'.color(:magenta)
205
230
  else '?'.color(:blue)
206
231
  end
207
- pretty_output += " #{message['line'].to_s.color(:blue)} #{message['linter'].color(:green)}: #{message['reason']} \n"
232
+ pretty_output << " #{message['line'].to_s.color(:blue)} #{message['linter'].color(:green)}: #{message['reason']} \n"
208
233
  end
209
234
  end
210
235
  pretty_output
211
236
  end
212
237
 
238
+ # String working directory
239
+ # @since 0.1.6
240
+ # @param path [String]
241
+ # @return [String]
242
+ def strip_working_dir(path)
243
+ path.gsub("#{@config.working_dir}/", '')
244
+ end
245
+
246
+ # Handle data and generate relevant_lints if appropriate
247
+ # @since 0.1.6
248
+ # @see #refine
249
+ # @param data [String, Hash]
250
+ # @return [String, Hash] String if error, Hash if success
251
+ def parse_data(data)
252
+ # Prevent abortive empty JSON.parse error
253
+ data = '{}' if data.blank?
254
+
255
+ return "Error from #{@task}: #{data}" if data.is_a?(String) && data.include?('No such')
213
256
 
257
+ data = JSON.parse(data) if data.is_a?(String)
258
+
259
+ @output[:relevant_lints] = relevant_lints( data, @git_files ) unless @git_files.blank?
260
+ data = @output[:relevant_lints] unless @settings[:commit].blank?
261
+ data
262
+ end
214
263
 
215
264
  end
216
265
  end
@@ -1,13 +1,13 @@
1
1
  module Maximus
2
+ # Evaluates quality of security on a Rails site
2
3
  # @since 0.1.0
3
4
  class Brakeman < Maximus::Lint
4
5
 
5
6
  # Brakeman (requires Rails)
6
7
  # @see Lint#initialize
7
8
  def result
8
-
9
9
  @task = 'brakeman'
10
- @path = @settings[:root_dir] if @path.blank?
10
+ @path = discover_path
11
11
 
12
12
  return unless is_rails? && temp_config(@task) && path_exists?(@path)
13
13
 
@@ -19,26 +19,15 @@ module Maximus
19
19
 
20
20
  unless brakeman.blank?
21
21
  bjson = JSON.parse(brakeman)
22
- @output[:ignored_warnings] = bjson['scan_info']['ignored_warnings']
23
- @output[:checks_performed] = bjson['scan_info']['checks_performed']
24
- @output[:number_of_controllers] = bjson['scan_info']['number_of_controllers']
25
- @output[:number_of_models] = bjson['scan_info']['number_of_models']
26
- @output[:number_of_templates] = bjson['scan_info']['number_of_templates']
27
- @output[:ruby_version] = bjson['scan_info']['ruby_version']
28
- @output[:rails_version] = bjson['scan_info']['rails_version']
22
+ basics(bjson)
29
23
  brakeman = {}
30
24
  ['warnings', 'errors'].each do |type|
31
25
  new_brakeman = bjson[type].group_by { |s| s['file'] }
32
26
  new_brakeman.each do |file, errors|
33
- if file
34
- brakeman[file.to_sym] = errors.map { |e| hash_for_brakeman(e, type) }
35
- end
27
+ next unless file
28
+ brakeman[file] = errors.map { |e| hash_for_brakeman(e, type) }
36
29
  end
37
30
  end
38
- # The output of brakeman is a mix of strings and symbols
39
- # but resetting the JSON like this standardizes everything.
40
- # @todo Better way to get around this?
41
- brakeman = JSON.parse(brakeman.to_json)
42
31
  end
43
32
 
44
33
  @output[:files_inspected] ||= files_inspected('rb', ' ')
@@ -53,14 +42,28 @@ module Maximus
53
42
  # @return [Hash]
54
43
  def hash_for_brakeman(error, type)
55
44
  {
56
- linter: error['warning_type'].delete(' '),
57
- severity: type.chomp('s'),
58
- reason: error['message'],
59
- column: 0,
60
- line: error['line'].to_i,
61
- confidence: error['confidence']
45
+ 'linter' => error['warning_type'].delete(' '),
46
+ 'severity' => type.chomp('s'),
47
+ 'reason' => error['message'],
48
+ 'column' => 0,
49
+ 'line' => error['line'].to_i,
50
+ 'confidence' => error['confidence']
62
51
  }
63
52
  end
64
53
 
54
+ # Pull out the general data brakeman provides
55
+ # @since 0.1.6
56
+ # @see #result
57
+ # @param brakeman_data [Hash]
58
+ def basics(brakeman_data)
59
+ @output[:ignored_warnings] = brakeman_data['scan_info']['ignored_warnings']
60
+ @output[:checks_performed] = brakeman_data['scan_info']['checks_performed']
61
+ @output[:number_of_controllers] = brakeman_data['scan_info']['number_of_controllers']
62
+ @output[:number_of_models] = brakeman_data['scan_info']['number_of_models']
63
+ @output[:number_of_templates] = brakeman_data['scan_info']['number_of_templates']
64
+ @output[:ruby_version] = brakeman_data['scan_info']['ruby_version']
65
+ @output[:rails_version] = brakeman_data['scan_info']['rails_version']
66
+ end
67
+
65
68
  end
66
69
  end
@@ -1,4 +1,5 @@
1
1
  module Maximus
2
+ # Evaluates quality of JavaScript
2
3
  # @since 0.1.0
3
4
  class Jshint < Maximus::Lint
4
5
 
@@ -6,14 +7,14 @@ module Maximus
6
7
  # @see Lint#initialize
7
8
  def result
8
9
  @task = 'jshint'
9
- @path = is_rails? ? "#{@settings[:root_dir]}/app/assets" : "#{@settings[:root_dir]}source/assets" if @path.blank?
10
+ @path = discover_path(@config.working_dir, 'javascripts', 'js')
10
11
 
11
12
  return unless temp_config(@task) && path_exists?(@path)
12
13
 
13
14
  node_module_exists(@task)
14
15
 
15
16
  jshint_cli = "jshint #{@path} --config=#{temp_config(@task)} --reporter=#{reporter_path('jshint.js')}"
16
- jshint_cli += " --exclude-path=#{temp_config(@settings[:jshintignore])}" if @settings.has_key?(:jshintignore)
17
+ jshint_cli << " --exclude-path=#{temp_config(@settings[:jshintignore])}" if @settings.key?(:jshintignore)
17
18
  jshint = `#{jshint_cli}`
18
19
 
19
20
  @output[:files_inspected] ||= files_inspected('js')
@@ -1,13 +1,13 @@
1
1
  module Maximus
2
+ # Evaluates quality of Rails methods
2
3
  # @since 0.1.0
3
4
  class Railsbp < Maximus::Lint
4
5
 
5
6
  # rails_best_practice (requires Rails)
6
7
  # @see Lint#initialize
7
8
  def result
8
-
9
9
  @task = 'railsbp'
10
- @path = @settings[:root_dir] if @path.blank?
10
+ @path = discover_path
11
11
 
12
12
  return unless is_rails? && temp_config(@task) && path_exists?(@path)
13
13
 
@@ -1,4 +1,5 @@
1
1
  module Maximus
2
+ # Evaluates quality of ruby
2
3
  # @since 0.1.0
3
4
  class Rubocop < Maximus::Lint
4
5
 
@@ -6,7 +7,7 @@ module Maximus
6
7
  # @see Lint#initialize
7
8
  def result
8
9
  @task = 'rubocop'
9
- @path = is_rails? ? "#{@settings[:root_dir]}/app" : "#{@settings[:root_dir]}/*.rb" if @path.blank?
10
+ @path = discover_path
10
11
 
11
12
  return unless temp_config(@task) && path_exists?(@path)
12
13