maximus 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,329 @@
1
+ module Maximus
2
+ # @since 0.1.3
3
+ # @attr_reader settings [Hash] all the options
4
+ # @attr_reader temp_files [Hash] Filename without extension => path to temp file
5
+ class Config
6
+
7
+ include Helper
8
+
9
+ attr_reader :settings, :temp_files
10
+
11
+ # Global options for all of maximus
12
+ #
13
+ # @param opts [Hash] options passed directly to config
14
+ # @option opts [Boolean] :is_dev (false) whether or not the class was initialized from the command line
15
+ # @option opts [String, Boolean, nil] :log ('log/maximus_git.log') path to log file
16
+ # If not set, logger outputs to STDOUT
17
+ # @option opts [String, Boolean] :git_log (false) path to log file or don't log
18
+ # The git gem is very noisey
19
+ # @option opts [String] :root_dir base directory
20
+ # @option opts [String] :domain ('http://localhost:3000') the host - used for Statistics
21
+ # @option opts [String, Integer] :port ('') port number - used for Statistics
22
+ # and appended to domain. If blank (false, empty string, etc.), will not
23
+ # append to domain
24
+ # @option opts [String, Array] :file_paths ('') path to files. Accepts glob notation
25
+ # @option opts [Hash] :paths ({home: '/'}) labeled relative path to URLs. Statistics only
26
+ # @option opts [String] :commit accepts sha, "working", "last", or "master".
27
+ # @option opts [String] :config_file ('maximus.yml') path to config file
28
+ # @return [void] this method is used to set up instance variables
29
+ def initialize(opts = {})
30
+ opts[:is_dev] ||= false
31
+
32
+ # Only set log file if it's set to true.
33
+ # Otherwise, allow it to be nil or a path
34
+ opts[:log] = 'log/maximus.log' if opts[:log].is_a?(TrueClass)
35
+
36
+ opts[:git_log] = false if opts[:git_log].nil?
37
+ opts[:git_log] = 'log/maximus_git.log' if opts[:git_log].is_a?(TrueClass)
38
+
39
+ # @see Helper#root_dir
40
+ opts[:root_dir] ||= root_dir
41
+ opts[:domain] ||= 'http://localhost'
42
+ opts[:port] ||= is_rails? ? 3000 : ''
43
+ opts[:paths] ||= { 'home' => '/' }
44
+
45
+ # Accounting for space-separated command line arrays
46
+ if opts[:paths].is_a?(Array)
47
+ new_paths = {}
48
+ opts[:paths].each do |p|
49
+ if p.split('/').length > 1
50
+ new_paths[p.split('/').last.to_s] = p
51
+ else
52
+ new_paths['home'] = '/'
53
+ end
54
+ end
55
+ opts[:paths] = new_paths
56
+ end
57
+
58
+ # What we're really interested in
59
+ @settings = opts
60
+
61
+ # Instance variables for Config class only
62
+ @temp_files = {}
63
+
64
+ conf_location = (opts[:config_file] && File.exist?(opts[:config_file])) ? opts[:config] : find_config
65
+
66
+ @yaml = YAML.load_file(conf_location)
67
+
68
+ # Match defaults
69
+ @yaml['domain'] ||= @settings[:domain]
70
+ @yaml['paths'] ||= @settings[:paths]
71
+ @yaml['port'] ||= @settings[:port]
72
+ set_families('lints', ['jshint', 'scsslint', 'rubocop', 'brakeman', 'railsbp'])
73
+ set_families('frontend', ['jshint', 'scsslint', 'phantomas', 'stylestats', 'wraith'])
74
+ set_families('backend', ['rubocop', 'brakeman', 'railsbp'])
75
+ set_families('ruby', ['rubocop', 'brakeman', 'railsbp'])
76
+ set_families('statistics', ['phantomas', 'stylestats', 'wraith'])
77
+ set_families('all', ['lints', 'statistics'])
78
+
79
+ # Override options with any defined in a discovered config file
80
+ evaluate_yaml
81
+ end
82
+
83
+ # Set global options or generate appropriate config files for lints or statistics
84
+ #
85
+ # @param yaml_data [Hash] (@yaml) loaded data from the discovered maximus config file
86
+ # @return [Hash] paths to temp config files and static options
87
+ # These should be deleted with destroy_temp after read and loaded
88
+ def evaluate_yaml(yaml_data = @yaml)
89
+ yaml_data.each do |key, value|
90
+ unless value.is_a?(FalseClass)
91
+ value = {} if value.is_a?(TrueClass)
92
+
93
+ case key
94
+
95
+ when 'jshint', 'JSHint', 'JShint'
96
+
97
+ # @todo DRY this up, but can't call it at the start because of the
98
+ # global config variables (last when statement in this switch)
99
+ value = load_config(value)
100
+
101
+ if yaml_data[key].is_a?(Hash) && yaml_data[key].has_key?['ignore']
102
+ jshintignore_file = []
103
+ yaml_data[key]['ignore'].each { |i| jshintignore_file << "#{i}\n" }
104
+ @settings[:jshintignore] = temp_it('jshintignore.json', jshintignore_file)
105
+ end
106
+ @settings[:jshint] = temp_it('jshint.json', value.to_json)
107
+
108
+ when 'scsslint', 'scss-lint', 'SCSSlint'
109
+ value = load_config(value)
110
+
111
+ @settings[:scsslint] = temp_it('scsslint.yml', value.to_yaml)
112
+
113
+ when 'rubocop', 'Rubocop', 'RuboCop'
114
+ value = load_config(value)
115
+
116
+ @settings[:rubocop] = temp_it('rubocop.yml', value.to_yaml)
117
+
118
+ when 'brakeman'
119
+ @settings[:brakeman] = yaml_data[key]
120
+
121
+ when 'rails_best_practice', 'railsbp'
122
+ @settings[:railsbp] = yaml_data[key]
123
+
124
+ when 'stylestats', 'Stylestats'
125
+ value = load_config(value)
126
+ @settings[:stylestats] = temp_it('stylestats.json', value.to_json)
127
+
128
+ when 'phantomas', 'Phantomas'
129
+ value = load_config(value)
130
+ @settings[:phantomas] = temp_it('phantomas.json', value.to_json)
131
+
132
+ when 'wraith', 'Wraith'
133
+ value = load_config(value)
134
+
135
+ @settings[:wraith] = {}
136
+ if value.include?('browser')
137
+ value['browser'].each do |browser, browser_value|
138
+ unless browser_value.is_a?(FalseClass)
139
+ new_data = {}
140
+ new_data['browser'] = []
141
+ new_data['browser'] << { browser.to_s => browser.to_s }
142
+
143
+ # Regardless of what's in the config, override with maximus,
144
+ # predictable namespacing
145
+ new_data['directory'] = "maximus_wraith_#{browser}"
146
+ new_data['history_dir'] = "maximus_wraith_history_#{browser}"
147
+
148
+ # @todo a snap file cannot be set in the config
149
+ snap_file = case browser
150
+ when 'casperjs' then 'casper'
151
+ when 'nojs' then 'nojs'
152
+ else 'snap'
153
+ end
154
+ new_data['snap_file'] = File.join(File.dirname(__FILE__), "config/wraith/#{snap_file}.js")
155
+
156
+ @settings[:wraith][browser.to_sym] = wraith_setup(new_data, "wraith_#{browser}")
157
+ end
158
+ end
159
+ else
160
+ value['browser'] = { 'phantomjs' => 'phantomjs' }
161
+ value['directory'] = 'maximus_wraith_phantomjs'
162
+ value['history_dir'] = 'maximus_wraith_history_phantomjs'
163
+ value['snap_file'] = File.join(File.dirname(__FILE__), "config/wraith/snap.js")
164
+ @settings[:wraith][:phantomjs] = wraith_setup(value)
165
+ end
166
+
167
+ # Configuration important to all of maximus
168
+ when 'is_dev', 'log', 'root_dir', 'domain', 'port', 'paths', 'commit'
169
+ @settings[key.to_sym] = yaml_data[key]
170
+ end
171
+ end
172
+ end
173
+
174
+ # Finally, we're done
175
+ @settings
176
+ end
177
+
178
+ # @return [Boolean]
179
+ def is_dev?
180
+ @settings[:is_dev]
181
+ end
182
+
183
+ # Defines base logger
184
+ #
185
+ # @param out [String, STDOUT] location for logging
186
+ # Accepts file path
187
+ # @return [Logger] self.log
188
+ def log
189
+ out = @settings[:log] || STDOUT
190
+ @log ||= Logger.new(out)
191
+ @log.level ||= Logger::INFO
192
+ @log
193
+ end
194
+
195
+ # Remove all or one created temporary config file
196
+ #
197
+ # @see #temp_it
198
+ # @see #yaml_evaluate
199
+ #
200
+ # @param filename [String] (nil) file to destroy
201
+ # If nil, destroy all temp files
202
+ # @return [void]
203
+ def destroy_temp(filename = nil)
204
+ return if @temp_files[filename.to_sym].blank?
205
+ if filename.nil?
206
+ @temp_files.each { |filename, file| file.unlink }
207
+ @temp_files = {}
208
+ else
209
+ @temp_files[filename.to_sym].unlink
210
+ @temp_files.delete(filename.to_sym)
211
+ end
212
+ end
213
+
214
+ # Combine domain with port if necessary
215
+ #
216
+ # @return [String] complete domain/host address
217
+ def domain
218
+ (!@settings[:port].blank? || @settings[:domain].include?(':')) ? "#{@settings[:domain]}:#{@settings[:port]}" : @settings[:domain]
219
+ end
220
+
221
+
222
+ private
223
+
224
+ # Allow shorthand to be declared for groups Maximus executions
225
+ #
226
+ # @example disable statistics
227
+ # @yaml['statistics'] = false
228
+ # set_families('statistics', ['phantomas', 'stylestats', 'wraith'])
229
+ #
230
+ # Sets as Boolean based on whether or not the queried label is `true`
231
+ # @param head_of_house [String] @yaml key and group label
232
+ # @param family [Array] group of other @yaml keys to be disabled
233
+ # @return [void] modified @yaml
234
+ def set_families(head_of_house, family)
235
+ if @yaml.has_key?(head_of_house)
236
+ family.each { |f| @yaml[f] = @yaml[head_of_house].is_a?(TrueClass) }
237
+ end
238
+ end
239
+
240
+ # Load config files if filename supplied
241
+ #
242
+ # @param value [Mixed] value from base config file
243
+ # @param [Hash] return blank hash if file not found so
244
+ # the reset of the process doesn't break
245
+ def load_config(value)
246
+ return value unless value.is_a?(String)
247
+ if File.exist?(value)
248
+ return YAML.load_file(value)
249
+ else
250
+ puts "#{value} not found"
251
+ return {}
252
+ end
253
+ end
254
+
255
+ # Create a temp file with config data
256
+ #
257
+ # Stores all temp files in @temp_files or self.temp_files
258
+ # In Hash with filename minus extension as the key.
259
+ #
260
+ # @param filename [String] the preferred name/identifier of the file
261
+ # @param data [Mixed] config data important to each lint or statistic
262
+ # @return [String] absolute path to new config file
263
+ def temp_it(filename, data)
264
+ ext = filename.split('.')
265
+ file = Tempfile.new([filename, ".#{ext[1]}"])
266
+ file.write(data)
267
+ file.close
268
+ @temp_files[ext[0].to_sym] = file
269
+ file.path
270
+ end
271
+
272
+ # Look for a maximus config file
273
+ #
274
+ # Checks ./maximus.yml, ./maximus.yaml, ./config/maximus.yaml in order.
275
+ # If there hasn't been a file discovered yet, checks ./config/maximus.yml
276
+ # and if there still isn't a file, load the default one included with the
277
+ # maximus gem.
278
+ #
279
+ # @return [String] absolute path to config file
280
+ def find_config
281
+ config_exists('maximus.yml') || config_exists('maximus.yaml') || config_exists('config/maximus.yaml') || check_default_config_path('maximus.yml')
282
+ end
283
+
284
+ # See if a config file exists
285
+ #
286
+ # @see #find_config
287
+ #
288
+ # This is used exclusively for the find_config method
289
+ # @param file [String] file name
290
+ # @return [String, FalseClass] if file is found return the absolute path
291
+ # otherwise return false so we can keep checking
292
+ def config_exists(file)
293
+ File.exist?(File.join(File.dirname(__FILE__), file)) ? File.join(File.dirname(__FILE__), file) : false
294
+ end
295
+
296
+ # Wraith is a complicated gem with significant configuration
297
+ #
298
+ # @see #yaml_evaluate
299
+ # @see #temp_it
300
+ #
301
+ # @param value [Hash] modified data from a wraith config or injected data
302
+ # @param name [String] ('wraith') config file name to write and eventually load
303
+ # @return [String] temp file path
304
+ def wraith_setup(value, name = 'phantomjs')
305
+
306
+ if @yaml.include?('urls')
307
+ value['domains'] = yaml_data['urls']
308
+ else
309
+ value['domains'] = {}
310
+ # @see #domain
311
+ value['domains']['main'] = domain
312
+ end
313
+
314
+ # Wraith requires this screen_width config to be present
315
+ value['screen_widths'] ||= [767, 1024, 1280]
316
+
317
+ value['paths'] = @yaml['paths']
318
+ value['threshold'] ||= 0
319
+
320
+ # Wraith requires config files have .yaml extensions
321
+ # https://github.com/BBC-News/wraith/blob/2aff771eba01b76e61600cccb2113869bfe16479/lib/wraith/wraith.rb
322
+ file = Tempfile.new([name, '.yaml'])
323
+ file.write(value.to_yaml)
324
+ file.close
325
+ file.path
326
+ end
327
+
328
+ end
329
+ end
@@ -1,8 +1,4 @@
1
1
  require 'git'
2
- require 'active_support'
3
- require 'active_support/core_ext/object/blank'
4
- require 'rainbow'
5
- require 'rainbow/ext/string'
6
2
 
7
3
  module Maximus
8
4
  # @since 0.1.0
@@ -12,29 +8,21 @@ module Maximus
12
8
 
13
9
  # Git management
14
10
  #
15
- # @param opts [Hash] the options to initialize and pass to other classes
16
- # @option opts [Boolean] :is_dev whether or not the class was initialized from the command line
17
- # @option opts [String] :log ('log/maximus_git.log') path to log file
18
- # @option opts [String] :root_dir base directory
19
- # @option opts [String] :base_url ('http://localhost:3000') the host - used for Statistics
20
- # @option opts [String, Integer] :port port number - used for Statistics
21
- # @option opts [String, Array] :path ('') path to files. Accepts glob notation
11
+ # Inherits settings from {Config#initialize}
12
+ # @param opts [Hash] options passed directly to config
13
+ # @option opts [Boolean] :is_dev (false) whether or not the class was initialized from the command line
14
+ # This is set here again in case only GitControl needs to be directly called (outside of command line)
15
+ # @option opts [Config object] :config custom Maximus::Config object
22
16
  # @option opts [String] :commit accepts sha, "working", "last", or "master".
23
- # Used in the command line
24
17
  # @return [void] this method is used to set up instance variables
25
18
  def initialize(opts = {})
26
19
  opts[:is_dev] ||= false
27
- opts[:log] = Logger.new('log/maximus_git.log') if opts[:log].nil?
28
- opts[:base_url] ||= 'http://localhost:3000'
29
- opts[:port] ||= ''
30
- opts[:root_dir] ||= root_dir
31
- log = opts[:log] ? log : nil
32
- @@log = mlog
33
- @@is_dev = opts[:is_dev]
34
- @opts = opts
35
-
36
- @psuedo_commit = (!@opts[:commit].blank? && @opts[:commit] == 'working')
37
- @g = Git.open(@opts[:root_dir], :log => log)
20
+
21
+ opts[:config] ||= Maximus::Config.new({commit: opts[:commit], is_dev: opts[:is_dev] })
22
+ @config ||= opts[:config]
23
+ @settings ||= @config.settings
24
+ @psuedo_commit = (!@settings[:commit].blank? && (@settings[:commit] == 'working' || @settings[:commit] == 'last' || @settings[:commit] == 'master') )
25
+ @g = Git.open(@settings[:root_dir], :log => @settings[:git_log])
38
26
  end
39
27
 
40
28
  # 30,000 foot view of a commit
@@ -74,12 +62,12 @@ module Maximus
74
62
  def compare(sha1 = master_commit.sha, sha2 = sha)
75
63
  diff_return = {}
76
64
 
77
- if @opts[:commit]
78
- sha1 = case @opts[:commit]
65
+ if @settings[:commit]
66
+ sha1 = case @settings[:commit]
79
67
  when 'master' then master_commit.sha
80
68
  when 'last' then @g.object('HEAD^').sha
81
69
  when 'working' then 'working'
82
- else @opts[:commit]
70
+ else @settings[:commit]
83
71
  end
84
72
  end
85
73
 
@@ -112,7 +100,7 @@ module Maximus
112
100
  unless files[child].blank?
113
101
  files[child].each do |c|
114
102
  # hack to ignore deleted files
115
- files[child] = new_lines[c].blank? ? [] : [ filename: "#{@opts[:root_dir]}/#{c}", changes: new_lines[c] ]
103
+ files[child] = new_lines[c].blank? ? [] : [ filename: "#{@settings[:root_dir]}/#{c}", changes: new_lines[c] ]
116
104
  end
117
105
  files[ext].concat(files[child])
118
106
  files.delete(child)
@@ -149,30 +137,23 @@ module Maximus
149
137
  git_shas.each do |sha, exts|
150
138
  # @todo better way to silence git, in case there's a real error?
151
139
  quietly { `git checkout #{sha} -b maximus_#{sha}` } unless @psuedo_commit
152
- puts sha.to_s.color(:blue) if @@is_dev
140
+ puts sha.to_s.color(:blue) if @config.is_dev?
153
141
  git_output[sha.to_sym] = {
154
142
  lints: {},
155
143
  statistics: {}
156
144
  }
157
145
  lints = git_output[sha.to_sym][:lints]
158
146
  statistics = git_output[sha.to_sym][:statistics]
159
- lint_opts = {
160
- is_dev: @@is_dev,
161
- root_dir: @opts[:root_dir],
162
- commit: !@opts[:commit].blank?
163
- }
164
- stat_opts = {
165
- is_dev: @@is_dev,
166
- base_url: @opts[:base_url],
167
- port: @opts[:port],
168
- root_dir: @opts[:root_dir]
169
- }
147
+ lint_opts = {}
170
148
 
171
149
  # This is where everything goes down
172
150
  exts.each do |ext, files|
173
151
  # For relevant_lines data
174
- lint_opts[:git_files] = files
175
- lint_opts[:path] = lint_file_paths(files, ext) if lint_by_path
152
+ lint_opts = {
153
+ git_files: files,
154
+ config: @config
155
+ }
156
+ lint_opts[:file_paths] = lint_file_paths(files, ext) if lint_by_path
176
157
  case ext
177
158
  when :scss
178
159
  lints[:scsslint] = Maximus::Scsslint.new(lint_opts).result
@@ -183,11 +164,11 @@ module Maximus
183
164
  # @todo stylestat is singular here because model name in Rails is singular.
184
165
  # But adding a .classify when it's converted to a model chops off the end s on 'phantomas',
185
166
  # which breaks the model name.
186
- statistics[:stylestat] = Maximus::Stylestats.new(stat_opts).result
167
+ statistics[:stylestat] = Maximus::Stylestats.new({config: @config}).result
187
168
 
188
169
  # @todo double pipe here is best way to say, if it's already run, don't run again, right?
189
- statistics[:phantomas] ||= Maximus::Phantomas.new(stat_opts).result
190
- statistics[:wraith] = Maximus::Wraith.new(stat_opts).result
170
+ statistics[:phantomas] ||= Maximus::Phantomas.new({config: @config}).result
171
+ statistics[:wraith] ||= Maximus::Wraith.new({config: @config}).result
191
172
  end
192
173
  when :js
193
174
  lints[:jshint] = Maximus::Jshint.new(lint_opts).result
@@ -195,14 +176,14 @@ module Maximus
195
176
  # Do not run statistics if called from command line
196
177
  if lint_opts[:commit].blank?
197
178
 
198
- statistics[:phantomas] = Maximus::Phantomas.new(stat_opts).result
179
+ statistics[:phantomas] ||= Maximus::Phantomas.new({config: @config}).result
199
180
 
200
181
  # @todo double pipe here is best way to say, if it's already run, don't run again, right?
201
- statistics[:wraith] ||= Maximus::Wraith.new(stat_opts).result
182
+ statistics[:wraith] ||= Maximus::Wraith.new({config: @config}).result
202
183
  end
203
184
  when :ruby
204
185
  lints[:rubocop] = Maximus::Rubocop.new(lint_opts).result
205
- lints[:railsbp] = Maximus::Railsbp.new(lint_opts).result
186
+ lints[:railsbp] ||= Maximus::Railsbp.new(lint_opts).result
206
187
  lints[:brakeman] = Maximus::Brakeman.new(lint_opts).result
207
188
  when :rails
208
189
  lints[:railsbp] ||= Maximus::Railsbp.new(lint_opts).result
@@ -280,6 +261,7 @@ module Maximus
280
261
  end
281
262
 
282
263
  # Store last commit as Ruby Git::Object
264
+ #
283
265
  # @param commitsha [String]
284
266
  # @return [Git::Object]
285
267
  def vccommit(commitsha = sha)
@@ -3,18 +3,15 @@ require 'rainbow/ext/string'
3
3
  require 'active_support'
4
4
  require 'active_support/core_ext/object/blank'
5
5
  require 'yaml'
6
+ require 'tempfile'
6
7
 
7
8
  module Maximus
8
9
  # @since 0.1.0
9
10
  module Helper
10
11
 
11
12
  # See if project linted is a Rails app
12
- # This will usually be stored as a class variable in the inherited class
13
- # @example class variable
14
- # @@is_rails = is_rails?
15
- #
16
- # @see Lint#initialize
17
13
  #
14
+ # This will usually be stored as a class variable in the inherited class
18
15
  # @return [Boolean]
19
16
  def is_rails?
20
17
  defined?(Rails)
@@ -45,15 +42,13 @@ module Maximus
45
42
  end
46
43
  end
47
44
 
48
- # Look for a custom config in the app's config/ directory;
49
- # otherwise, use the built-in one.
50
- # @todo best practice that this inherits the @opts from the model it's being included in?
45
+ # Look for a file in the config directory
51
46
  #
52
- # @param filename [String]
53
- # @return [String] absolute path to the reporter file
54
- def check_default(filename)
55
- user_file = "#{@opts[:root_dir]}/config/#{filename}"
56
- File.exist?(user_file) ? user_file : File.join(File.dirname(__FILE__), "config/#{filename}")
47
+ # @since 0.1.0
48
+ # @param file [String] filename with extension to search for
49
+ # @return [String] path to default config file or file in user's directory
50
+ def check_default_config_path(file)
51
+ File.exist?(file) ? file : File.join(File.dirname(__FILE__), "config/#{file}")
57
52
  end
58
53
 
59
54
  # Grab the absolute path of the reporter file
@@ -61,7 +56,7 @@ module Maximus
61
56
  # @param filename [String]
62
57
  # @return [String] absolute path to the reporter file
63
58
  def reporter_path(filename)
64
- File.join(File.dirname(__FILE__),"reporter/#{filename}")
59
+ File.join(File.dirname(__FILE__), "reporter/#{filename}")
65
60
  end
66
61
 
67
62
  # Find all files that were linted by extension
@@ -96,6 +91,7 @@ module Maximus
96
91
 
97
92
  # Edit and save a YAML file
98
93
  #
94
+ # @param yaml_location [String] YAML absolute file path
99
95
  # @return [void]
100
96
  def edit_yaml(yaml_location, &block)
101
97
  d = YAML.load_file(yaml_location)
@@ -112,15 +108,6 @@ module Maximus
112
108
  STDIN.gets
113
109
  end
114
110
 
115
- # Defines base logger
116
- #
117
- # @return [Logger] @@log for logging use
118
- def mlog
119
- @@log ||= Logger.new(STDOUT)
120
- @@log.level ||= Logger::INFO
121
- @@log
122
- end
123
-
124
111
  # Convert the array from lines_added into spelled-out ranges
125
112
  # This is a GitControl helper but it's used in Lint
126
113
  # @see GitControl#lines_added