maximus 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +66 -0
  6. data/Rakefile +2 -0
  7. data/bin/maximus +15 -0
  8. data/lib/maximus.rb +25 -0
  9. data/lib/maximus/cli.rb +106 -0
  10. data/lib/maximus/config/.jshintignore +2 -0
  11. data/lib/maximus/config/jshint.json +9 -0
  12. data/lib/maximus/config/phantomas.json +4 -0
  13. data/lib/maximus/config/phantomas_urls.yaml +1 -0
  14. data/lib/maximus/config/rubocop.yml +1007 -0
  15. data/lib/maximus/config/scsslint.yml +58 -0
  16. data/lib/maximus/config/stylestats.json +30 -0
  17. data/lib/maximus/config/wraith.yaml +56 -0
  18. data/lib/maximus/config/wraith/casper.js +20 -0
  19. data/lib/maximus/config/wraith/nojs.js +85 -0
  20. data/lib/maximus/config/wraith/snap.js +85 -0
  21. data/lib/maximus/constants.rb +3 -0
  22. data/lib/maximus/git_control.rb +255 -0
  23. data/lib/maximus/helper.rb +137 -0
  24. data/lib/maximus/lint.rb +201 -0
  25. data/lib/maximus/lints/brakeman.rb +61 -0
  26. data/lib/maximus/lints/jshint.rb +20 -0
  27. data/lib/maximus/lints/railsbp.rb +51 -0
  28. data/lib/maximus/lints/rubocop.rb +18 -0
  29. data/lib/maximus/lints/scsslint.rb +17 -0
  30. data/lib/maximus/rake_tasks.rb +13 -0
  31. data/lib/maximus/reporter/git-lines.sh +57 -0
  32. data/lib/maximus/reporter/jshint.js +28 -0
  33. data/lib/maximus/reporter/rubocop.rb +49 -0
  34. data/lib/maximus/statistic.rb +65 -0
  35. data/lib/maximus/statistics/phantomas.rb +32 -0
  36. data/lib/maximus/statistics/stylestats.rb +111 -0
  37. data/lib/maximus/statistics/wraith.rb +88 -0
  38. data/lib/maximus/tasks/be.rake +35 -0
  39. data/lib/maximus/tasks/fe.rake +30 -0
  40. data/lib/maximus/tasks/maximus.rake +39 -0
  41. data/lib/maximus/tasks/statistic.rake +26 -0
  42. data/lib/maximus/version.rb +3 -0
  43. data/maximus.gemspec +32 -0
  44. data/roadmap.md +17 -0
  45. metadata +243 -0
@@ -0,0 +1,58 @@
1
+ scss_files: 'app/assets/stylesheets/'
2
+
3
+ # exclude: 'app/assets/stylesheets/admin/'
4
+
5
+ format: 'JSON'
6
+
7
+ linters:
8
+ Compass::*:
9
+ enabled: true
10
+ Compass::PropertyWithMixin:
11
+ ignore:
12
+ - 'inline-block'
13
+ - 'border-radius'
14
+ - 'box-shadow'
15
+ - 'opacity'
16
+ - 'text-shadow'
17
+ Indentation:
18
+ enabled: false
19
+ character: tab
20
+ severity: warning
21
+ width: 1
22
+ NameFormat:
23
+ convention: BEM
24
+ enabled: false
25
+ SelectorFormat:
26
+ enabled: false
27
+ DeclarationOrder:
28
+ enabled: false
29
+ EmptyLineBetweenBlocks:
30
+ enabled: false
31
+ NestingDepth:
32
+ max_depth: 6
33
+ severity: error
34
+ SelectorDepth:
35
+ max_depth: 5
36
+ severity: error
37
+ LeadingZero:
38
+ enabled: false
39
+ PlaceholderInExtend:
40
+ enabled: false
41
+ PropertySortOrder:
42
+ enabled: false
43
+ QualifyingElement:
44
+ enabled: true
45
+ allow_element_with_attribute: true
46
+ SpaceAfterPropertyColon:
47
+ enabled: false
48
+ UnnecessaryParentReference:
49
+ severity: error
50
+ VendorPrefixes:
51
+ exclude:
52
+ - '-webkit-tap-highlight-color'
53
+ - '-webkit-font-smoothing'
54
+ - '-moz-osx-font-smoothing'
55
+ - '-webkit-appearance'
56
+ - '-moz-placeholder'
57
+ - '-ms-input-placeholder'
58
+ - '-moz-pre-wrap'
@@ -0,0 +1,30 @@
1
+ {
2
+ "published": false,
3
+ "paths": false,
4
+ "stylesheets": false,
5
+ "styleElements": true,
6
+ "size": true,
7
+ "dataUriSize": true,
8
+ "ratioOfDataUriSize": true,
9
+ "gzippedSize": false,
10
+ "simplicity": true,
11
+ "rules": true,
12
+ "selectors": true,
13
+ "mostIdentifier": true,
14
+ "mostIdentifierSelector": true,
15
+ "lowestCohesion": true,
16
+ "lowestCohesionSelector": true,
17
+ "totalUniqueFontSizes": true,
18
+ "uniqueFontSize": true,
19
+ "totalUniqueColors": true,
20
+ "uniqueColor": true,
21
+ "idSelectors": true,
22
+ "universalSelectors": true,
23
+ "unqualifiedAttributeSelectors": true,
24
+ "javascriptSpecificSelectors": "[#\\.]js\\-",
25
+ "importantKeywords": true,
26
+ "floatProperties": true,
27
+ "mediaQueries": true,
28
+ "propertiesCount": 10,
29
+ "requestOptions": {}
30
+ }
@@ -0,0 +1,56 @@
1
+ #Headless browser option
2
+ browser:
3
+ phantomjs: "phantomjs"
4
+ # slimerjs: "slimerjs"
5
+ # phantomjs: "casperjs"
6
+
7
+ #If you want to have multiple snapping files, set the file name here
8
+ snap_file: "config/wraith/snap.js"
9
+ # snap_file: "config/wraith/nojs.js"
10
+
11
+ # Type the name of the directory that shots will be stored in
12
+ directory: 'maximus_wraith'
13
+ history_dir: 'maximus_wraith_history'
14
+
15
+ domains:
16
+ main: "http://localhost:3000"
17
+
18
+ #Type screen widths below, here are a couple of examples
19
+ screen_widths:
20
+ - 767
21
+ - 1024
22
+ - 1280
23
+
24
+ #Type page URL paths below
25
+ paths:
26
+ home: /
27
+
28
+ # Casper/component example config
29
+ # paths:
30
+ # home:
31
+ # path: /
32
+ # selector: '.site-brand'
33
+
34
+ #Amount of fuzz ImageMagick will use
35
+ fuzz: '20%'
36
+
37
+ #Set the filename of the spider file to use, if not specified it will fallback to spider.txt
38
+ # spider_file: bbc_co_uk_spider.txt
39
+
40
+ #Set the number of days to keep the site spider file
41
+ spider_days:
42
+ - 10
43
+
44
+ #A list of URLs to skip when spidering. Ruby regular expressions can be
45
+ #used, if prefixed with !ruby/regexp as defined in the YAML Cookbook
46
+ #http://www.yaml.org/YAML_for_ruby.html#regexps
47
+ #
48
+ # spider_skips:
49
+ # - /foo/bar.html # Matches /foo/bar.html explcitly
50
+ # - !ruby/regexp /^\/baz\// # Matches any URLs that start with /baz
51
+
52
+ #Choose how results are displayed, by default alphanumeric. Different screen widths are always grouped.
53
+ #alphanumeric - all paths (with, and without, a difference) are shown, sorted by path
54
+ #diffs_first - all paths (with, and without, a difference) are shown, sorted by difference size (largest first)
55
+ #diffs_only - only paths with a difference are shown, sorted by difference size (largest first)
56
+ #mode: diffs_first
@@ -0,0 +1,20 @@
1
+ var system = require('system');
2
+ var page = require('webpage').create();
3
+ var fs = require('fs');
4
+ var casper = require("casper").create();
5
+
6
+ var url = casper.cli.get(0);
7
+ var view_port_width = casper.cli.get(1);
8
+ var image_name = casper.cli.get(2);
9
+ var selector = casper.cli.get(3);
10
+
11
+ casper.start(url, function() {
12
+ this.viewport(view_port_width, 1500).then(function(){
13
+ this.wait(2000, function() {
14
+ this.captureSelector(image_name, selector);
15
+ console.log('Snapping ' + url + ' at width ' + view_port_width);
16
+ });
17
+ });
18
+ });
19
+
20
+ casper.run();
@@ -0,0 +1,85 @@
1
+ var system = require('system');
2
+ var page = require('webpage').create();
3
+ var fs = require('fs');
4
+
5
+ if (system.args.length === 3) {
6
+ console.log('Usage: snap.js <some URL> <view port width> <target image name>');
7
+ phantom.exit();
8
+ }
9
+
10
+ var url = system.args[1];
11
+ var image_name = system.args[3];
12
+ var view_port_width = system.args[2];
13
+ var current_requests = 0;
14
+ var last_request_timeout;
15
+ var final_timeout;
16
+
17
+
18
+ page.viewportSize = { width: view_port_width, height: 1500};
19
+ page.settings = { loadImages: true, javascriptEnabled: false };
20
+
21
+ // If you want to use additional phantomjs commands, place them here
22
+ page.settings.userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.17';
23
+
24
+ // You can place custom headers here, example below.
25
+ // page.customHeaders = {
26
+
27
+ // 'X-Candy-OVERRIDE': 'https://api.live.bbc.co.uk/'
28
+
29
+ // };
30
+
31
+ // If you want to set a cookie, just add your details below in the following way.
32
+
33
+ // phantom.addCookie({
34
+ // 'name': 'ckns_policy',
35
+ // 'value': '111',
36
+ // 'domain': '.bbc.co.uk'
37
+ // });
38
+ // phantom.addCookie({
39
+ // 'name': 'locserv',
40
+ // 'value': '1#l1#i=6691484:n=Oxford+Circus:h=e@w1#i=8:p=London@d1#1=l:2=e:3=e:4=2@n1#r=40',
41
+ // 'domain': '.bbc.co.uk'
42
+ // });
43
+
44
+ page.onResourceRequested = function(req) {
45
+ current_requests += 1;
46
+ };
47
+
48
+ page.onResourceReceived = function(res) {
49
+ if (res.stage === 'end') {
50
+ current_requests -= 1;
51
+ debounced_render();
52
+ }
53
+ };
54
+
55
+ page.open(url, function(status) {
56
+ if (status !== 'success') {
57
+ console.log('Error with page ' + url);
58
+ phantom.exit();
59
+ }
60
+ });
61
+
62
+
63
+ function debounced_render() {
64
+ clearTimeout(last_request_timeout);
65
+ clearTimeout(final_timeout);
66
+
67
+ // If there's no more ongoing resource requests, wait for 1 second before
68
+ // rendering, just in case the page kicks off another request
69
+ if (current_requests < 1) {
70
+ clearTimeout(final_timeout);
71
+ last_request_timeout = setTimeout(function() {
72
+ console.log('Snapping ' + url + ' at width ' + view_port_width);
73
+ page.render(image_name);
74
+ phantom.exit();
75
+ }, 1000);
76
+ }
77
+
78
+ // Sometimes, straggling requests never make it back, in which
79
+ // case, timeout after 5 seconds and render the page anyway
80
+ final_timeout = setTimeout(function() {
81
+ console.log('Snapping ' + url + ' at width ' + view_port_width);
82
+ page.render(image_name);
83
+ phantom.exit();
84
+ }, 5000);
85
+ }
@@ -0,0 +1,85 @@
1
+ var system = require('system');
2
+ var page = require('webpage').create();
3
+ var fs = require('fs');
4
+
5
+ if (system.args.length === 3) {
6
+ console.log('Usage: snap.js <some URL> <view port width> <target image name>');
7
+ phantom.exit();
8
+ }
9
+
10
+ var url = system.args[1];
11
+ var image_name = system.args[3];
12
+ var view_port_width = system.args[2];
13
+ var current_requests = 0;
14
+ var last_request_timeout;
15
+ var final_timeout;
16
+
17
+
18
+ page.viewportSize = { width: view_port_width, height: 1500};
19
+ page.settings = { loadImages: true, javascriptEnabled: true };
20
+
21
+ // If you want to use additional phantomjs commands, place them here
22
+ page.settings.userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.17';
23
+
24
+ // You can place custom headers here, example below.
25
+ // page.customHeaders = {
26
+
27
+ // 'X-Candy-OVERRIDE': 'https://api.live.bbc.co.uk/'
28
+
29
+ // };
30
+
31
+ // If you want to set a cookie, just add your details below in the following way.
32
+
33
+ // phantom.addCookie({
34
+ // 'name': 'ckns_policy',
35
+ // 'value': '111',
36
+ // 'domain': '.bbc.co.uk'
37
+ // });
38
+ // phantom.addCookie({
39
+ // 'name': 'locserv',
40
+ // 'value': '1#l1#i=6691484:n=Oxford+Circus:h=e@w1#i=8:p=London@d1#1=l:2=e:3=e:4=2@n1#r=40',
41
+ // 'domain': '.bbc.co.uk'
42
+ // });
43
+
44
+ page.onResourceRequested = function(req) {
45
+ current_requests += 1;
46
+ };
47
+
48
+ page.onResourceReceived = function(res) {
49
+ if (res.stage === 'end') {
50
+ current_requests -= 1;
51
+ debounced_render();
52
+ }
53
+ };
54
+
55
+ page.open(url, function(status) {
56
+ if (status !== 'success') {
57
+ console.log('Error with page ' + url);
58
+ phantom.exit();
59
+ }
60
+ });
61
+
62
+
63
+ function debounced_render() {
64
+ clearTimeout(last_request_timeout);
65
+ clearTimeout(final_timeout);
66
+
67
+ // If there's no more ongoing resource requests, wait for 1 second before
68
+ // rendering, just in case the page kicks off another request
69
+ if (current_requests < 1) {
70
+ clearTimeout(final_timeout);
71
+ last_request_timeout = setTimeout(function() {
72
+ console.log('Snapping ' + url + ' at width ' + view_port_width);
73
+ page.render(image_name);
74
+ phantom.exit();
75
+ }, 1000);
76
+ }
77
+
78
+ // Sometimes, straggling requests never make it back, in which
79
+ // case, timeout after 5 seconds and render the page anyway
80
+ final_timeout = setTimeout(function() {
81
+ console.log('Snapping ' + url + ' at width ' + view_port_width);
82
+ page.render(image_name);
83
+ phantom.exit();
84
+ }, 5000);
85
+ }
@@ -0,0 +1,3 @@
1
+ # Global application constants.
2
+ module Maximus
3
+ end
@@ -0,0 +1,255 @@
1
+ require 'git'
2
+ require 'active_support'
3
+ require 'active_support/core_ext/object/blank'
4
+ require 'rainbow'
5
+ require 'rainbow/ext/string'
6
+
7
+ module Maximus
8
+ class GitControl
9
+
10
+ include Helper
11
+
12
+ def initialize(opts = {})
13
+ opts[:is_dev] ||= false
14
+ opts[:log] = Logger.new('log/maximus_git.log') if opts[:log].nil?
15
+ opts[:base_url] ||= 'http://localhost:3000'
16
+ opts[:port] ||= ''
17
+ opts[:root_dir] ||= root_dir
18
+ log = opts[:log] ? log : nil
19
+ @@log = mlog
20
+ @@is_dev = opts[:is_dev]
21
+ @opts = opts
22
+
23
+ @psuedo_commit = (!@opts[:commit].blank? && @opts[:commit] == 'working')
24
+ @g = Git.open(@opts[:root_dir], :log => log)
25
+ end
26
+
27
+ # Returns Hash of commit data
28
+ def commit_export(commitsha = sha)
29
+ ce_commit = vccommit(commitsha)
30
+ ce_diff = diff(ce_commit, @g.object('HEAD^'))
31
+ {
32
+ commitsha: commitsha,
33
+ branch: branch,
34
+ message: ce_commit.message,
35
+ remote_repo: remote,
36
+ git_author: ce_commit.author.name,
37
+ git_author_email: ce_commit.author.email,
38
+ diff: ce_diff
39
+ }
40
+ end
41
+
42
+ # Compare two commits and get line number ranges of changed patches
43
+ # Returns Hash grouped by file extension (defined in assoc) => { filename, changes: (changed line ranges) }
44
+ # Example: 'sha' => { rb: {filename: 'file.rb', changes: { ['0..4'], ['10..20'] } }}
45
+ def compare(sha1 = master_commit.sha, sha2 = sha)
46
+ diff_return = {}
47
+
48
+ if @opts[:commit]
49
+ sha1 = case @opts[:commit]
50
+ when 'master' then master_commit.sha
51
+ when 'last' then @g.object('HEAD^').sha
52
+ when 'working' then 'working'
53
+ else @opts[:commit]
54
+ end
55
+ end
56
+
57
+ # if working directory, just have a single item array
58
+ # the space here is important because git-lines checks for a second arg,
59
+ # and if one is present, it runs git diff without a commit
60
+ # or a comparison to a commit
61
+ git_diff = @psuedo_commit ? ['working directory'] : `git rev-list #{sha1}..#{sha2} --no-merges`.split("\n")
62
+
63
+ # Include the first sha because rev-list is doing a traversal
64
+ # So sha1 is never included
65
+ git_diff << sha1 unless @psuedo_commit
66
+
67
+ # Reverse so that we go in chronological order
68
+ git_diff.reverse.each do |git_sha|
69
+ new_lines = lines_added(git_sha)
70
+
71
+ # Grab all files in that commit and group them by extension
72
+ # If working copy, just give the diff names of the files changed
73
+ files = @psuedo_commit ? `git diff --name-only` : `git show --pretty="format:" --name-only #{git_sha}`
74
+ files = files.split("\n").group_by { |f| f.split('.').pop }
75
+
76
+ # Don't worry about files that we don't have a lint or a statistic for
77
+ flat_associations = associations.clone.flatten(2)
78
+ files.delete_if { |k,v| !flat_associations.include?(k) || k.nil? }
79
+
80
+ associations.each do |ext, related|
81
+ files[ext] ||= []
82
+ related.each do |child|
83
+ unless files[child].blank?
84
+ files[child].each do |c|
85
+ # hack to ignore deleted files
86
+ files[child] = new_lines[c].blank? ? [] : [ filename: "#{@opts[:root_dir]}/#{c}", changes: new_lines[c] ]
87
+ end
88
+ files[ext].concat(files[child])
89
+ files.delete(child)
90
+ end
91
+ end
92
+ end
93
+ files.delete_if { |k,v| v.blank? }
94
+ diff_return[git_sha.to_sym] = files
95
+ end
96
+ diff_return
97
+ end
98
+
99
+ # Run appropriate lint for every sha in commit history
100
+ # Creates new branch based on each sha, then deletes it
101
+ # Different from above method as it returns the entire lint, not just the lines relevant to commit
102
+ # Returns Hash with all data grouped by task
103
+ # Example: { 'sha': { lints: { scsslint: { files_inspec... }, statisti... } }, 'sha...' }
104
+ def lints_and_stats(lint_by_path = false, git_shas = compare)
105
+ return false if git_shas.blank?
106
+ base_branch = branch
107
+ git_output = {}
108
+ git_shas.each do |sha, exts|
109
+ # TODO - better way to silence git, in case there's a real error?
110
+ quietly { `git checkout #{sha} -b maximus_#{sha}` } unless @psuedo_commit
111
+ puts sha.to_s.color(:blue) if @@is_dev
112
+ git_output[sha.to_sym] = {
113
+ lints: {},
114
+ statistics: {}
115
+ }
116
+ lints = git_output[sha.to_sym][:lints]
117
+ statistics = git_output[sha.to_sym][:statistics]
118
+ lint_opts = {
119
+ is_dev: @@is_dev,
120
+ root_dir: @opts[:root_dir],
121
+ commit: !@opts[:commit].blank?
122
+ }
123
+ stat_opts = {
124
+ is_dev: @@is_dev,
125
+ base_url: @opts[:base_url],
126
+ port: @opts[:port],
127
+ root_dir: @opts[:root_dir]
128
+ }
129
+ # This is where everything goes down
130
+ exts.each do |ext, files|
131
+ # For relevant_lines data
132
+ lint_opts[:git_files] = files
133
+ lint_opts[:path] = lint_file_paths(files, ext) if lint_by_path
134
+ case ext
135
+ when :scss
136
+ lints[:scsslint] = Maximus::Scsslint.new(lint_opts).result
137
+
138
+ # Do not run statistics if called by rake task :compare
139
+ if lint_opts[:commit].blank?
140
+
141
+ # stylestat is singular here because model name in Rails is singular.
142
+ # But adding a .classify when it's converted to a model chops off the end s on 'phantomas',
143
+ # which breaks the model name. This could be a TODO
144
+ statistics[:stylestat] = Maximus::Stylestats.new(stat_opts).result
145
+
146
+ # TODO - double pipe here is best way to say, if it's already run, don't run again, right?
147
+ statistics[:phantomas] ||= Maximus::Phantomas.new(stat_opts).result
148
+ statistics[:wraith] = Maximus::Wraith.new(stat_opts).result
149
+ end
150
+ when :js
151
+ lints[:jshint] = Maximus::Jshint.new(lint_opts).result
152
+
153
+ # Do not run statistics if called by rake task :compare
154
+ if lint_opts[:commit].blank?
155
+
156
+ statistics[:phantomas] = Maximus::Phantomas.new(stat_opts).result
157
+
158
+ # TODO - double pipe here is best way to say, if it's already run, don't run again, right?
159
+ statistics[:wraith] ||= Maximus::Wraith.new(stat_opts).result
160
+ end
161
+ when :ruby
162
+ lints[:rubocop] = Maximus::Rubocop.new(lint_opts).result
163
+ lints[:railsbp] = Maximus::Railsbp.new(lint_opts).result
164
+ lints[:brakeman] = Maximus::Brakeman.new(lint_opts).result
165
+ when :rails
166
+ lints[:railsbp] ||= Maximus::Railsbp.new(lint_opts).result
167
+ end
168
+ end
169
+ # TODO - better way to silence git, in case there's a real error?
170
+ quietly {
171
+ @g.branch(base_branch).checkout
172
+ @g.branch("maximus_#{sha}").delete
173
+ } unless @psuedo_commit
174
+ end
175
+ git_output
176
+ end
177
+
178
+
179
+ protected
180
+
181
+ # Get list of file paths
182
+ # Returns String delimited by comma or space
183
+ def lint_file_paths(files, ext)
184
+ file_list = files.map { |f| f[:filename] }.compact
185
+ # Lints accept files differently
186
+ ext == :ruby ? file_list.join(' ') : file_list.join(',')
187
+ end
188
+
189
+ # Returns Array of ranges by lines added in a commit by file name
190
+ # {'filename' => ['0..10', '11..14']}
191
+ def lines_added(git_sha)
192
+ new_lines = {}
193
+ lines_added = `#{File.join(File.dirname(__FILE__), 'reporter/git-lines.sh')} #{git_sha}`.split("\n")
194
+ lines_added.each do |filename|
195
+ fsplit = filename.split(':')
196
+ # if file isn't already part of the array
197
+ new_lines[fsplit[0]] ||= []
198
+ new_lines[fsplit[0]] << fsplit[1] unless fsplit[1].nil?
199
+ # no repeats
200
+ new_lines[fsplit[0]].uniq!
201
+ end
202
+ new_lines.delete("/dev/null")
203
+ new_lines
204
+ end
205
+
206
+ # Get last commit on current branch
207
+ # Returns String
208
+ def sha
209
+ @g.object('HEAD').sha
210
+ end
211
+
212
+ # Get branch name
213
+ # Returns String
214
+ def branch
215
+ `env -i git rev-parse --abbrev-ref HEAD`.strip!
216
+ end
217
+
218
+ # Get last commit on the master branch
219
+ # Returns Git::Object
220
+ def master_commit
221
+ @g.branches[:master].gcommit
222
+ end
223
+
224
+ # Store last commit as Ruby Git::Object
225
+ # Returns Git::Object
226
+ def vccommit(commitsha = sha)
227
+ @g.gcommit(commitsha)
228
+ end
229
+
230
+ # Get general stats of commit on HEAD versus last commit on master branch
231
+ # Roadmap - include lines_added in this method's output
232
+ # Returns Git::Diff
233
+ def diff(new_commit = vccommit, old_commit = master_commit)
234
+ @g.diff(new_commit, old_commit).stats
235
+ end
236
+
237
+ # Get remote URL
238
+ # Returns String or nil if remotes is blank
239
+ def remote
240
+ @g.remotes.first.url unless @g.remotes.blank?
241
+ end
242
+
243
+ # Define associations to linters based on file extension
244
+ # Returns Hash of linters and extension arrays
245
+ def associations
246
+ {
247
+ scss: ['scss', 'sass'],
248
+ js: ['js'],
249
+ ruby: ['rb', 'Gemfile', 'lock', 'yml', 'Rakefile', 'ru', 'rdoc'],
250
+ rails: ['slim', 'haml']
251
+ }
252
+ end
253
+
254
+ end
255
+ end