├── .gitignore
├── .travis.yml
├── CHANGELOG
├── Dockerfile
├── LICENSE
├── README.md
├── integration
└── todo
│ ├── .gitignore
│ ├── Gemfile
│ ├── Gemfile.lock
│ ├── README.md
│ ├── Rakefile
│ ├── app
│ ├── assets
│ │ ├── config
│ │ │ └── manifest.js
│ │ ├── images
│ │ │ └── .keep
│ │ ├── javascripts
│ │ │ ├── application.js
│ │ │ ├── cable.js
│ │ │ ├── channels
│ │ │ │ └── .keep
│ │ │ ├── todo_items.coffee
│ │ │ └── todo_lists.coffee
│ │ └── stylesheets
│ │ │ ├── application.css
│ │ │ ├── scaffolds.scss
│ │ │ ├── todo_items.scss
│ │ │ └── todo_lists.scss
│ ├── channels
│ │ └── application_cable
│ │ │ ├── channel.rb
│ │ │ └── connection.rb
│ ├── controllers
│ │ ├── application_controller.rb
│ │ ├── concerns
│ │ │ └── .keep
│ │ ├── todo_items_controller.rb
│ │ └── todo_lists_controller.rb
│ ├── helpers
│ │ ├── application_helper.rb
│ │ ├── todo_items_helper.rb
│ │ └── todo_lists_helper.rb
│ ├── jobs
│ │ └── application_job.rb
│ ├── mailers
│ │ └── application_mailer.rb
│ ├── models
│ │ ├── application_record.rb
│ │ ├── concerns
│ │ │ └── .keep
│ │ ├── todo_item.rb
│ │ └── todo_list.rb
│ └── views
│ │ ├── layouts
│ │ ├── application.html.erb
│ │ ├── mailer.html.erb
│ │ └── mailer.text.erb
│ │ ├── todo_items
│ │ ├── _form.html.erb
│ │ └── _todo_item.html.erb
│ │ └── todo_lists
│ │ ├── _form.html.erb
│ │ ├── _todo_list.json.jbuilder
│ │ ├── edit.html.erb
│ │ ├── index.html.erb
│ │ ├── index.json.jbuilder
│ │ ├── new.html.erb
│ │ ├── show.html.erb
│ │ └── show.json.jbuilder
│ ├── bin
│ ├── bundle
│ ├── rails
│ ├── rake
│ ├── setup
│ ├── spring
│ ├── update
│ └── yarn
│ ├── config.ru
│ ├── config
│ ├── application.rb
│ ├── boot.rb
│ ├── cable.yml
│ ├── database.yml
│ ├── environment.rb
│ ├── environments
│ │ ├── development.rb
│ │ ├── production.rb
│ │ └── test.rb
│ ├── initializers
│ │ ├── application_controller_renderer.rb
│ │ ├── assets.rb
│ │ ├── backtrace_silencers.rb
│ │ ├── cookies_serializer.rb
│ │ ├── filter_parameter_logging.rb
│ │ ├── inflections.rb
│ │ ├── mime_types.rb
│ │ └── wrap_parameters.rb
│ ├── locales
│ │ └── en.yml
│ ├── puma.rb
│ ├── routes.rb
│ ├── secrets.yml
│ └── spring.rb
│ ├── db
│ ├── migrate
│ │ ├── 20180818210306_create_todo_lists.rb
│ │ ├── 20180819121311_create_todo_items.rb
│ │ └── 20180819123754_add_completed_at_to_todo_items.rb
│ ├── schema.rb
│ └── seeds.rb
│ ├── lib
│ ├── assets
│ │ └── .keep
│ └── tasks
│ │ └── .keep
│ ├── log
│ └── .keep
│ ├── package.json
│ ├── public
│ ├── 404.html
│ ├── 422.html
│ ├── 500.html
│ ├── apple-touch-icon-precomposed.png
│ ├── apple-touch-icon.png
│ ├── favicon.ico
│ └── robots.txt
│ ├── sonar-project.properties
│ ├── test
│ ├── application_system_test_case.rb
│ ├── controllers
│ │ ├── .keep
│ │ ├── todo_items_controller_test.rb
│ │ └── todo_lists_controller_test.rb
│ ├── fixtures
│ │ ├── .keep
│ │ ├── files
│ │ │ └── .keep
│ │ ├── todo_items.yml
│ │ └── todo_lists.yml
│ ├── helpers
│ │ └── .keep
│ ├── integration
│ │ └── .keep
│ ├── mailers
│ │ └── .keep
│ ├── models
│ │ ├── .keep
│ │ ├── todo_item_test.rb
│ │ └── todo_list_test.rb
│ ├── system
│ │ ├── .keep
│ │ └── todo_lists_test.rb
│ └── test_helper.rb
│ ├── tmp
│ └── .keep
│ └── vendor
│ └── .keep
├── pom.xml
├── sonar-project.properties
└── src
├── main
├── java
│ └── com
│ │ └── fortitudetec
│ │ └── sonar
│ │ └── plugins
│ │ └── ruby
│ │ ├── PathResolver.java
│ │ ├── Ruby.java
│ │ ├── RubyPlugin.java
│ │ ├── RubyRuleProfile.java
│ │ ├── RubyRulesDefinition.java
│ │ ├── metrics
│ │ ├── ClassCountParser.java
│ │ ├── CombinedCoverageSensor.java
│ │ ├── CommentCountParser.java
│ │ └── RubyMetricsSensor.java
│ │ ├── model
│ │ ├── RubocopIssue.java
│ │ ├── RubocopPosition.java
│ │ └── RubocopRule.java
│ │ ├── rubocop
│ │ ├── RubocopExecutor.java
│ │ ├── RubocopExecutorConfig.java
│ │ ├── RubocopParser.java
│ │ ├── RubocopSensor.java
│ │ └── model
│ │ │ ├── RubocopFile.java
│ │ │ ├── RubocopOffense.java
│ │ │ └── RubocopResult.java
│ │ ├── simplecov
│ │ ├── SimpleCovParser.java
│ │ └── SimpleCovSensor.java
│ │ └── squid
│ │ ├── RubyFootPrint.java
│ │ └── RubyRecognizer.java
└── resources
│ └── rubocop
│ └── rubocop.yml
└── test
├── java
└── com
│ └── fortitudetec
│ └── sonar
│ └── plugins
│ └── ruby
│ ├── PathResolverTest.java
│ ├── RubyPluginTest.java
│ ├── RubyRuleProfileTest.java
│ ├── RubyRulesDefinitionTest.java
│ ├── RubyTest.java
│ ├── metrics
│ ├── ClassCountParserTest.java
│ ├── CombinedCoverageSensorTest.java
│ ├── CommentCountParserTest.java
│ └── RubyMetricsSensorTest.java
│ ├── rubocop
│ ├── RubocopParserTest.java
│ └── RubocopSensorTest.java
│ ├── simplecov
│ ├── SimpleCovParserTest.java
│ └── SimpleCovSensorTest.java
│ └── utils
│ └── TestUtils.java
└── resources
├── result.json
├── test_controller.rb
└── unknown_file.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Maven template
2 | target/
3 |
4 | ### Java template
5 | # Log file
6 | *.log
7 |
8 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
9 | hs_err_pid*
10 | ### JetBrains template
11 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
12 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
13 |
14 | # User-specific stuff:
15 | .idea/
16 | *.iml
17 |
18 | ## File-based project format:
19 | *.iws
20 |
21 | ## Plugin-specific files:
22 |
23 | # IntelliJ
24 | /out/
25 |
26 | # mpeltonen/sbt-idea plugin
27 | .idea_modules/
28 |
29 | # Crashlytics plugin (for Android Studio and IntelliJ)
30 | com_crashlytics_export_strings.xml
31 | crashlytics.properties
32 | crashlytics-build.properties
33 | fabric.properties
34 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # https://docs.travis-ci.com/user/sonarcloud/
2 |
3 | language: java
4 |
5 | addons:
6 | sonarcloud:
7 | organization: fortitudetec
8 | token:
9 | secure: 527af96a234edc72a58c766b3ef7ccc1d2159018
10 |
11 | script:
12 | # builds the project
13 | # runs tests with coverage
14 | # execute SonarCloud analysis
15 | - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install sonar:sonar
16 |
--------------------------------------------------------------------------------
/CHANGELOG:
--------------------------------------------------------------------------------
1 | v 1.1.0:
2 | - Support other test runners besides RSpec
3 | - Added a sample Rails application for testing the plugin with SonarQube
4 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM sonarqube:7.1
2 |
3 | COPY target/sonar-ruby-plugin-1.1.0.jar /opt/sonarqube/extensions/plugins/
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Fortitude Technologies
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## This project is no longer maintained and is archived
2 |
3 | # Sonar Ruby Plugin
4 | SonarQube plugin for analyzing Ruby files
5 |
6 | 
7 | [](https://travis-ci.com/fortitudetec/sonar-ruby-plugin)
8 |
9 | ## Requirements
10 | * Rubocop (https://github.com/bbatsov/rubocop)
11 | * Simplecov (https://github.com/colszowka/simplecov)
12 | * Tested with v6.5, v6.6, v7.1
13 |
14 | ## Overview
15 | This is plugin for SonarQube 5.6+ for analysing projects with Ruby content that supports:
16 |
17 | * Rubocop for code quality information
18 | * Simplecov for unit test coverage information
19 | * NCLOC metric generation
20 |
21 | ## Configuration
22 |
23 | ### Example project configuration
24 | This is an example of what a project configuration file (`sonar-project.properties`) could look like:
25 | ```
26 | sonar.projectKey=company:my-application
27 | sonar.projectName=My Application
28 | sonar.projectVersion=1.0
29 | sonar.sourceEncoding=UTF-8
30 | sonar.sources=src/app
31 | sonar.exclusions=**/node_modules/**,**/*.spec.ts
32 | sonar.tests=src/app
33 | sonar.test.inclusions=**/*.spec.ts
34 |
35 | sonar.ruby.file.suffixes=rb,ruby
36 | sonar.ruby.coverage.reportPath=coverage/.resultset.json
37 | sonar.ruby.coverage.framework=RSpec
38 | sonar.ruby.rubocopConfig=.rubocop.yml
39 | sonar.ruby.rubocop=/usr/bin/rubocop
40 | sonar.ruby.rubocop.reportPath=rubocop-result.json
41 | sonar.ruby.rubocop.filePath=.
42 | ```
43 |
44 | ## Installation
45 |
46 | ### Manual Installation
47 | Here are the steps to manually install this plugin for use in SonarQube:
48 |
49 | 1. Download the plugin jar
50 | 2. Copy the jar into the SonarQube plugin directory (e.g. /opt/sonarqube/extensions/plugins)
51 | 3. Restart SonarQube
52 |
53 | ### Update Center Installation
54 | TODO: Need to get this plugin added to SonarQube's update center
55 |
56 | ## Running the analysis
57 | In order to run the analysis for Ruby you will need to utilize the [sonar-scanner](https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner) application.
58 |
59 | * Run rubocop making sure that the json results file is created
60 | e.g. `rubocop --format=json --out=rubocop-result.json`
61 |
62 | * Run your specs (make sure that your sonarqube config points to the right coverage directory )
63 | e.g. `bundle exec rspec spec`
64 | *Make sure that your sonarqube config points to the right coverage directory e.g. `spec/coverage/.resultset.json`*
65 |
66 | * Make sure you have a sonar-project.properties file in the root of your project directory
67 | * run `sonar-scanner`
68 |
69 | ## Contributing
70 | #### 1. GitHub issue
71 | To request a new feature, [create a GitHub issue](https://github.com/fortitudetec/sonar-ruby-plugin/issues/new). Even if you plan to implement it yourself and submit it back to the community, please create an issue to be sure that we can follow up on it.
72 |
73 | #### 2. Pull Request
74 | To submit a contribution, create a pull request for this repository.
75 |
76 | ## License
77 |
78 | Copyright 2017-2018 Fortitude Technologies.
79 |
80 | Licensed under the [MIT License](https://en.wikipedia.org/wiki/MIT_License)
81 |
--------------------------------------------------------------------------------
/integration/todo/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2 | #
3 | # If you find yourself ignoring temporary files generated by your text editor
4 | # or operating system, you probably want to add a global ignore instead:
5 | # git config --global core.excludesfile '~/.gitignore_global'
6 |
7 | # Ignore bundler config.
8 | /.bundle
9 |
10 | # Ignore the default SQLite database.
11 | /db/*.sqlite3
12 | /db/*.sqlite3-journal
13 |
14 | # Ignore all logfiles and tempfiles.
15 | /log/*
16 | /tmp/*
17 | !/log/.keep
18 | !/tmp/.keep
19 |
20 | /node_modules
21 | /yarn-error.log
22 |
23 | .byebug_history
24 |
25 | .idea
26 | coverage
27 |
--------------------------------------------------------------------------------
/integration/todo/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | git_source(:github) do |repo_name|
4 | repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
5 | "https://github.com/#{repo_name}.git"
6 | end
7 |
8 |
9 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
10 | gem 'rails', '~> 5.2.1'
11 | # Use sqlite3 as the database for Active Record
12 | gem 'sqlite3'
13 | # Use Puma as the app server
14 | gem 'puma'
15 | # Use SCSS for stylesheets
16 | gem 'sass-rails'
17 | # Use Uglifier as compressor for JavaScript assets
18 | gem 'uglifier', '>= 1.3.0'
19 |
20 | # Use CoffeeScript for .coffee assets and views
21 | gem 'coffee-rails'
22 | # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
23 | gem 'turbolinks'
24 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
25 | gem 'jbuilder'
26 |
27 | # Use Capistrano for deployment
28 | # gem 'capistrano-rails', group: :development
29 |
30 | group :development, :test do
31 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console
32 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
33 | # Adds support for Capybara system testing and selenium driver
34 | gem 'capybara'
35 | gem 'minitest'
36 | gem 'minitest-reporters'
37 | gem 'selenium-webdriver'
38 | gem 'simplecov'
39 | end
40 |
41 | group :development do
42 | # Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
43 | gem 'web-console', '>= 3.3.0'
44 | gem 'listen', '>= 3.0.5', '< 3.2'
45 | gem 'rubocop'
46 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
47 | gem 'spring'
48 | gem 'spring-watcher-listen'
49 | end
50 |
51 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
52 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
53 |
--------------------------------------------------------------------------------
/integration/todo/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | actioncable (5.2.1)
5 | actionpack (= 5.2.1)
6 | nio4r (~> 2.0)
7 | websocket-driver (>= 0.6.1)
8 | actionmailer (5.2.1)
9 | actionpack (= 5.2.1)
10 | actionview (= 5.2.1)
11 | activejob (= 5.2.1)
12 | mail (~> 2.5, >= 2.5.4)
13 | rails-dom-testing (~> 2.0)
14 | actionpack (5.2.1)
15 | actionview (= 5.2.1)
16 | activesupport (= 5.2.1)
17 | rack (~> 2.0)
18 | rack-test (>= 0.6.3)
19 | rails-dom-testing (~> 2.0)
20 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
21 | actionview (5.2.1)
22 | activesupport (= 5.2.1)
23 | builder (~> 3.1)
24 | erubi (~> 1.4)
25 | rails-dom-testing (~> 2.0)
26 | rails-html-sanitizer (~> 1.0, >= 1.0.3)
27 | activejob (5.2.1)
28 | activesupport (= 5.2.1)
29 | globalid (>= 0.3.6)
30 | activemodel (5.2.1)
31 | activesupport (= 5.2.1)
32 | activerecord (5.2.1)
33 | activemodel (= 5.2.1)
34 | activesupport (= 5.2.1)
35 | arel (>= 9.0)
36 | activestorage (5.2.1)
37 | actionpack (= 5.2.1)
38 | activerecord (= 5.2.1)
39 | marcel (~> 0.3.1)
40 | activesupport (5.2.1)
41 | concurrent-ruby (~> 1.0, >= 1.0.2)
42 | i18n (>= 0.7, < 2)
43 | minitest (~> 5.1)
44 | tzinfo (~> 1.1)
45 | addressable (2.5.2)
46 | public_suffix (>= 2.0.2, < 4.0)
47 | ansi (1.5.0)
48 | arel (9.0.0)
49 | ast (2.4.0)
50 | bindex (0.5.0)
51 | builder (3.2.3)
52 | byebug (10.0.2)
53 | capybara (3.6.0)
54 | addressable
55 | mini_mime (>= 0.1.3)
56 | nokogiri (~> 1.8)
57 | rack (>= 1.6.0)
58 | rack-test (>= 0.6.3)
59 | xpath (~> 3.1)
60 | childprocess (0.9.0)
61 | ffi (~> 1.0, >= 1.0.11)
62 | coffee-rails (4.2.2)
63 | coffee-script (>= 2.2.0)
64 | railties (>= 4.0.0)
65 | coffee-script (2.4.1)
66 | coffee-script-source
67 | execjs
68 | coffee-script-source (1.12.2)
69 | concurrent-ruby (1.0.5)
70 | crass (1.0.4)
71 | docile (1.3.1)
72 | erubi (1.7.1)
73 | execjs (2.7.0)
74 | ffi (1.9.25)
75 | globalid (0.4.1)
76 | activesupport (>= 4.2.0)
77 | i18n (1.1.0)
78 | concurrent-ruby (~> 1.0)
79 | jaro_winkler (1.5.1)
80 | jbuilder (2.7.0)
81 | activesupport (>= 4.2.0)
82 | multi_json (>= 1.2)
83 | json (2.1.0)
84 | listen (3.1.5)
85 | rb-fsevent (~> 0.9, >= 0.9.4)
86 | rb-inotify (~> 0.9, >= 0.9.7)
87 | ruby_dep (~> 1.2)
88 | loofah (2.2.2)
89 | crass (~> 1.0.2)
90 | nokogiri (>= 1.5.9)
91 | mail (2.7.0)
92 | mini_mime (>= 0.1.1)
93 | marcel (0.3.2)
94 | mimemagic (~> 0.3.2)
95 | method_source (0.9.0)
96 | mimemagic (0.3.2)
97 | mini_mime (1.0.1)
98 | mini_portile2 (2.3.0)
99 | minitest (5.11.3)
100 | minitest-reporters (1.3.2)
101 | ansi
102 | builder
103 | minitest (>= 5.0)
104 | ruby-progressbar
105 | multi_json (1.13.1)
106 | nio4r (2.3.1)
107 | nokogiri (1.8.4)
108 | mini_portile2 (~> 2.3.0)
109 | parallel (1.12.1)
110 | parser (2.5.1.2)
111 | ast (~> 2.4.0)
112 | powerpack (0.1.2)
113 | public_suffix (3.0.3)
114 | puma (3.12.0)
115 | rack (2.0.5)
116 | rack-test (1.1.0)
117 | rack (>= 1.0, < 3)
118 | rails (5.2.1)
119 | actioncable (= 5.2.1)
120 | actionmailer (= 5.2.1)
121 | actionpack (= 5.2.1)
122 | actionview (= 5.2.1)
123 | activejob (= 5.2.1)
124 | activemodel (= 5.2.1)
125 | activerecord (= 5.2.1)
126 | activestorage (= 5.2.1)
127 | activesupport (= 5.2.1)
128 | bundler (>= 1.3.0)
129 | railties (= 5.2.1)
130 | sprockets-rails (>= 2.0.0)
131 | rails-dom-testing (2.0.3)
132 | activesupport (>= 4.2.0)
133 | nokogiri (>= 1.6)
134 | rails-html-sanitizer (1.0.4)
135 | loofah (~> 2.2, >= 2.2.2)
136 | railties (5.2.1)
137 | actionpack (= 5.2.1)
138 | activesupport (= 5.2.1)
139 | method_source
140 | rake (>= 0.8.7)
141 | thor (>= 0.19.0, < 2.0)
142 | rainbow (3.0.0)
143 | rake (12.3.1)
144 | rb-fsevent (0.10.3)
145 | rb-inotify (0.9.10)
146 | ffi (>= 0.5.0, < 2)
147 | rubocop (0.58.2)
148 | jaro_winkler (~> 1.5.1)
149 | parallel (~> 1.10)
150 | parser (>= 2.5, != 2.5.1.1)
151 | powerpack (~> 0.1)
152 | rainbow (>= 2.2.2, < 4.0)
153 | ruby-progressbar (~> 1.7)
154 | unicode-display_width (~> 1.0, >= 1.0.1)
155 | ruby-progressbar (1.10.0)
156 | ruby_dep (1.5.0)
157 | rubyzip (1.2.1)
158 | sass (3.5.7)
159 | sass-listen (~> 4.0.0)
160 | sass-listen (4.0.0)
161 | rb-fsevent (~> 0.9, >= 0.9.4)
162 | rb-inotify (~> 0.9, >= 0.9.7)
163 | sass-rails (5.0.7)
164 | railties (>= 4.0.0, < 6)
165 | sass (~> 3.1)
166 | sprockets (>= 2.8, < 4.0)
167 | sprockets-rails (>= 2.0, < 4.0)
168 | tilt (>= 1.1, < 3)
169 | selenium-webdriver (3.14.0)
170 | childprocess (~> 0.5)
171 | rubyzip (~> 1.2)
172 | simplecov (0.16.1)
173 | docile (~> 1.1)
174 | json (>= 1.8, < 3)
175 | simplecov-html (~> 0.10.0)
176 | simplecov-html (0.10.2)
177 | spring (2.0.2)
178 | activesupport (>= 4.2)
179 | spring-watcher-listen (2.0.1)
180 | listen (>= 2.7, < 4.0)
181 | spring (>= 1.2, < 3.0)
182 | sprockets (3.7.2)
183 | concurrent-ruby (~> 1.0)
184 | rack (> 1, < 3)
185 | sprockets-rails (3.2.1)
186 | actionpack (>= 4.0)
187 | activesupport (>= 4.0)
188 | sprockets (>= 3.0.0)
189 | sqlite3 (1.3.13)
190 | thor (0.20.0)
191 | thread_safe (0.3.6)
192 | tilt (2.0.8)
193 | turbolinks (5.1.1)
194 | turbolinks-source (~> 5.1)
195 | turbolinks-source (5.1.0)
196 | tzinfo (1.2.5)
197 | thread_safe (~> 0.1)
198 | uglifier (4.1.18)
199 | execjs (>= 0.3.0, < 3)
200 | unicode-display_width (1.4.0)
201 | web-console (3.6.2)
202 | actionview (>= 5.0)
203 | activemodel (>= 5.0)
204 | bindex (>= 0.4.0)
205 | railties (>= 5.0)
206 | websocket-driver (0.7.0)
207 | websocket-extensions (>= 0.1.0)
208 | websocket-extensions (0.1.3)
209 | xpath (3.1.0)
210 | nokogiri (~> 1.8)
211 |
212 | PLATFORMS
213 | ruby
214 |
215 | DEPENDENCIES
216 | byebug
217 | capybara
218 | coffee-rails
219 | jbuilder
220 | listen (>= 3.0.5, < 3.2)
221 | minitest
222 | minitest-reporters
223 | puma
224 | rails (~> 5.2.1)
225 | rubocop
226 | sass-rails
227 | selenium-webdriver
228 | simplecov
229 | spring
230 | spring-watcher-listen
231 | sqlite3
232 | turbolinks
233 | tzinfo-data
234 | uglifier (>= 1.3.0)
235 | web-console (>= 3.3.0)
236 |
237 | BUNDLED WITH
238 | 1.14.6
239 |
--------------------------------------------------------------------------------
/integration/todo/README.md:
--------------------------------------------------------------------------------
1 | # README
2 |
3 | This README would normally document whatever steps are necessary to get the
4 | application up and running.
5 |
6 | Things you may want to cover:
7 |
8 | * Ruby version
9 |
10 | * System dependencies
11 |
12 | * Configuration
13 |
14 | * Database creation
15 |
16 | * Database initialization
17 |
18 | * How to run the test suite
19 |
20 | * Services (job queues, cache servers, search engines, etc.)
21 |
22 | * Deployment instructions
23 |
24 | * ...
25 |
--------------------------------------------------------------------------------
/integration/todo/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require_relative 'config/application'
5 |
6 | Rails.application.load_tasks
7 |
--------------------------------------------------------------------------------
/integration/todo/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../javascripts .js
3 | //= link_directory ../stylesheets .css
4 |
--------------------------------------------------------------------------------
/integration/todo/app/assets/images/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/app/assets/images/.keep
--------------------------------------------------------------------------------
/integration/todo/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
5 | // vendor/assets/javascripts directory can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file. JavaScript code in this file should be added after the last require_* statement.
9 | //
10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11 | // about supported directives.
12 | //
13 | //= require rails-ujs
14 | //= require turbolinks
15 | //= require_tree .
16 |
--------------------------------------------------------------------------------
/integration/todo/app/assets/javascripts/cable.js:
--------------------------------------------------------------------------------
1 | // Action Cable provides the framework to deal with WebSockets in Rails.
2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command.
3 | //
4 | //= require action_cable
5 | //= require_self
6 | //= require_tree ./channels
7 |
8 | (function() {
9 | this.App || (this.App = {});
10 |
11 | App.cable = ActionCable.createConsumer();
12 |
13 | }).call(this);
14 |
--------------------------------------------------------------------------------
/integration/todo/app/assets/javascripts/channels/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/app/assets/javascripts/channels/.keep
--------------------------------------------------------------------------------
/integration/todo/app/assets/javascripts/todo_items.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/integration/todo/app/assets/javascripts/todo_lists.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/integration/todo/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
6 | * vendor/assets/stylesheets directory can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10 | * files in this directory. Styles in this file should be added after the last require_* statement.
11 | * It is generally better to create a new file per style scope.
12 | *
13 | *= require_tree .
14 | *= require_self
15 | */
16 |
--------------------------------------------------------------------------------
/integration/todo/app/assets/stylesheets/scaffolds.scss:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #fff;
3 | color: #333;
4 | margin: 33px;
5 | font-family: verdana, arial, helvetica, sans-serif;
6 | font-size: 13px;
7 | line-height: 18px;
8 | }
9 |
10 | p, ol, ul, td {
11 | font-family: verdana, arial, helvetica, sans-serif;
12 | font-size: 13px;
13 | line-height: 18px;
14 | }
15 |
16 | pre {
17 | background-color: #eee;
18 | padding: 10px;
19 | font-size: 11px;
20 | }
21 |
22 | a {
23 | color: #000;
24 |
25 | &:visited {
26 | color: #666;
27 | }
28 |
29 | &:hover {
30 | color: #fff;
31 | background-color: #000;
32 | }
33 | }
34 |
35 | th {
36 | padding-bottom: 5px;
37 | }
38 |
39 | td {
40 | padding: 0 5px 7px;
41 | }
42 |
43 | div {
44 | &.field, &.actions {
45 | margin-bottom: 10px;
46 | }
47 | }
48 |
49 | #notice {
50 | color: green;
51 | }
52 |
53 | .field_with_errors {
54 | padding: 2px;
55 | background-color: red;
56 | display: table;
57 | }
58 |
59 | #error_explanation {
60 | width: 450px;
61 | border: 2px solid red;
62 | padding: 7px 7px 0;
63 | margin-bottom: 20px;
64 | background-color: #f0f0f0;
65 |
66 | h2 {
67 | text-align: left;
68 | font-weight: bold;
69 | padding: 5px 5px 5px 15px;
70 | font-size: 12px;
71 | margin: -7px -7px 0;
72 | background-color: #c00;
73 | color: #fff;
74 | }
75 |
76 | ul li {
77 | font-size: 12px;
78 | list-style: square;
79 | }
80 | }
81 |
82 | label {
83 | display: block;
84 | }
85 |
--------------------------------------------------------------------------------
/integration/todo/app/assets/stylesheets/todo_items.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the todo_items controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/integration/todo/app/assets/stylesheets/todo_lists.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the todo_lists controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/integration/todo/app/channels/application_cable/channel.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Channel < ActionCable::Channel::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/integration/todo/app/channels/application_cable/connection.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Connection < ActionCable::Connection::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/integration/todo/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | protect_from_forgery with: :exception
3 | end
4 |
--------------------------------------------------------------------------------
/integration/todo/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/integration/todo/app/controllers/todo_items_controller.rb:
--------------------------------------------------------------------------------
1 | class TodoItemsController < ApplicationController
2 | before_action :set_todo_list
3 | before_action :set_todo_item, except: [:create]
4 |
5 | def create
6 | @todo_item = @todo_list.todo_items.create(todo_item_params)
7 | redirect_to @todo_list
8 | end
9 |
10 | def destroy
11 | @todo_item = @todo_list.todo_items.find(params[:id])
12 | if @todo_item.destroy
13 | flash[:success] = "Todo List item was deleted."
14 | else
15 | flash[:error] = "Todo List item could not be deleted."
16 | end
17 | redirect_to @todo_list
18 | end
19 |
20 | def complete
21 | @todo_item.update_attribute(:completed_at, Time.now)
22 | redirect_to @todo_list, notice: "Todo item completed"
23 | end
24 |
25 | private
26 |
27 | def set_todo_list
28 | @todo_list = TodoList.find(params[:todo_list_id])
29 | end
30 |
31 | def set_todo_list_item
32 | @todo_item = @todo_list.todo_items.find(params[:id])
33 | end
34 |
35 | def todo_item_params
36 | params[:todo_item].permit(:content)
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/integration/todo/app/controllers/todo_lists_controller.rb:
--------------------------------------------------------------------------------
1 | class TodoListsController < ApplicationController
2 | before_action :set_todo_list, only: [:show, :edit, :update, :destroy]
3 |
4 | # GET /todo_lists
5 | # GET /todo_lists.json
6 | def index
7 | @todo_lists = TodoList.all
8 | end
9 |
10 | # GET /todo_lists/1
11 | # GET /todo_lists/1.json
12 | def show
13 | end
14 |
15 | # GET /todo_lists/new
16 | def new
17 | @todo_list = TodoList.new
18 | end
19 |
20 | # GET /todo_lists/1/edit
21 | def edit
22 | end
23 |
24 | # POST /todo_lists
25 | # POST /todo_lists.json
26 | def create
27 | @todo_list = TodoList.new(todo_list_params)
28 |
29 | respond_to do |format|
30 | if @todo_list.save
31 | format.html { redirect_to @todo_list, notice: 'Todo list was successfully created.' }
32 | format.json { render :show, status: :created, location: @todo_list }
33 | else
34 | format.html { render :new }
35 | format.json { render json: @todo_list.errors, status: :unprocessable_entity }
36 | end
37 | end
38 | end
39 |
40 | # PATCH/PUT /todo_lists/1
41 | # PATCH/PUT /todo_lists/1.json
42 | def update
43 | respond_to do |format|
44 | if @todo_list.update(todo_list_params)
45 | format.html { redirect_to @todo_list, notice: 'Todo list was successfully updated.' }
46 | format.json { render :show, status: :ok, location: @todo_list }
47 | else
48 | format.html { render :edit }
49 | format.json { render json: @todo_list.errors, status: :unprocessable_entity }
50 | end
51 | end
52 | end
53 |
54 | # DELETE /todo_lists/1
55 | # DELETE /todo_lists/1.json
56 | def destroy
57 | @todo_list.destroy
58 | respond_to do |format|
59 | format.html { redirect_to todo_lists_url, notice: 'Todo list was successfully destroyed.' }
60 | format.json { head :no_content }
61 | end
62 | end
63 |
64 | private
65 | # Use callbacks to share common setup or constraints between actions.
66 | def set_todo_list
67 | @todo_list = TodoList.find(params[:id])
68 | end
69 |
70 | # Never trust parameters from the scary internet, only allow the white list through.
71 | def todo_list_params
72 | params.require(:todo_list).permit(:title, :description)
73 | end
74 | end
75 |
--------------------------------------------------------------------------------
/integration/todo/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/integration/todo/app/helpers/todo_items_helper.rb:
--------------------------------------------------------------------------------
1 | module TodoItemsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/integration/todo/app/helpers/todo_lists_helper.rb:
--------------------------------------------------------------------------------
1 | module TodoListsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/integration/todo/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | class ApplicationJob < ActiveJob::Base
2 | end
3 |
--------------------------------------------------------------------------------
/integration/todo/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | default from: 'from@example.com'
3 | layout 'mailer'
4 | end
5 |
--------------------------------------------------------------------------------
/integration/todo/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/integration/todo/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/app/models/concerns/.keep
--------------------------------------------------------------------------------
/integration/todo/app/models/todo_item.rb:
--------------------------------------------------------------------------------
1 | class TodoItem < ApplicationRecord
2 | belongs_to :todo_list
3 |
4 | def completed?
5 | !completed_at.blank?
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/integration/todo/app/models/todo_list.rb:
--------------------------------------------------------------------------------
1 | class TodoList < ApplicationRecord
2 | has_many :todo_items
3 | end
4 |
--------------------------------------------------------------------------------
/integration/todo/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Todo
5 | <%= csrf_meta_tags %>
6 |
7 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
8 | <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
9 |
10 |
11 |
12 | <%= yield %>
13 |
14 |
15 |
--------------------------------------------------------------------------------
/integration/todo/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/integration/todo/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/integration/todo/app/views/todo_items/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_for([@todo_list, @todo_list.todo_items.build]) do |f| %>
2 | <%= f.text_field :content, placeholder: "New Todo" %>
3 | <%= f.submit %>
4 | <% end %>
--------------------------------------------------------------------------------
/integration/todo/app/views/todo_items/_todo_item.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <% if todo_item.completed? %>
3 |
4 | <%= link_to "Mark as Complete", complete_todo_list_todo_item_path(@todo_list, todo_item.id), method: :patch %>
5 |
6 |
7 |
<%= todo_item.content %>
8 |
9 |
10 | <%= link_to "Delete", todo_list_todo_item_path(@todo_list, todo_item.id), method: :delete, data: { confirm: "Are you sure?" } %>
11 |
12 | <% else %>
13 |
14 | <%= link_to "Mark as Complete", complete_todo_list_todo_item_path(@todo_list, todo_item.id), method: :patch %>
15 |
16 |
17 |
<%= todo_item.content %>
18 |
19 |
20 | <%= link_to "Delete", todo_list_todo_item_path(@todo_list, todo_item.id), method: :delete, data: { confirm: "Are you sure?" } %>
21 |
22 | <% end %>
23 |
--------------------------------------------------------------------------------
/integration/todo/app/views/todo_lists/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with(model: todo_list, local: true) do |form| %>
2 | <% if todo_list.errors.any? %>
3 |
4 |
<%= pluralize(todo_list.errors.count, "error") %> prohibited this todo_list from being saved:
5 |
6 |
7 | <% todo_list.errors.full_messages.each do |message| %>
8 | <%= message %>
9 | <% end %>
10 |
11 |
12 | <% end %>
13 |
14 |
15 | <%= form.label :title %>
16 | <%= form.text_field :title, id: :todo_list_title %>
17 |
18 |
19 |
20 | <%= form.label :description %>
21 | <%= form.text_area :description, id: :todo_list_description %>
22 |
23 |
24 |
25 | <%= form.submit %>
26 |
27 | <% end %>
28 |
--------------------------------------------------------------------------------
/integration/todo/app/views/todo_lists/_todo_list.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.extract! todo_list, :id, :title, :description, :created_at, :updated_at
2 | json.url todo_list_url(todo_list, format: :json)
3 |
--------------------------------------------------------------------------------
/integration/todo/app/views/todo_lists/edit.html.erb:
--------------------------------------------------------------------------------
1 | Editing Todo List
2 |
3 | <%= render 'form', todo_list: @todo_list %>
4 |
5 | <%= link_to 'Show', @todo_list %> |
6 | <%= link_to 'Back', todo_lists_path %>
7 |
--------------------------------------------------------------------------------
/integration/todo/app/views/todo_lists/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= notice %>
2 |
3 | Todo Lists
4 |
5 |
6 |
7 |
8 | Title
9 | Description
10 |
11 |
12 |
13 |
14 |
15 | <% @todo_lists.each do |todo_list| %>
16 |
17 | <%= todo_list.title %>
18 | <%= todo_list.description %>
19 | <%= link_to 'Show', todo_list %>
20 | <%= link_to 'Edit', edit_todo_list_path(todo_list) %>
21 | <%= link_to 'Destroy', todo_list, method: :delete, data: { confirm: 'Are you sure?' } %>
22 |
23 | <% end %>
24 |
25 |
26 |
27 |
28 |
29 | <%= link_to 'New Todo List', new_todo_list_path %>
30 |
--------------------------------------------------------------------------------
/integration/todo/app/views/todo_lists/index.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.array! @todo_lists, partial: 'todo_lists/todo_list', as: :todo_list
2 |
--------------------------------------------------------------------------------
/integration/todo/app/views/todo_lists/new.html.erb:
--------------------------------------------------------------------------------
1 | New Todo List
2 |
3 | <%= render 'form', todo_list: @todo_list %>
4 |
5 | <%= link_to 'Back', todo_lists_path %>
6 |
--------------------------------------------------------------------------------
/integration/todo/app/views/todo_lists/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= notice %>
2 |
3 |
4 | Title:
5 | <%= @todo_list.title %>
6 |
7 |
8 |
9 | Description:
10 | <%= @todo_list.description %>
11 |
12 |
13 |
14 | <%= render @todo_list.todo_items %>
15 |
16 | <%= render "todo_items/form" %>
17 |
18 |
19 |
20 | <%= link_to 'Edit', edit_todo_list_path(@todo_list) %> |
21 | <%= link_to 'Back', todo_lists_path %>
22 |
--------------------------------------------------------------------------------
/integration/todo/app/views/todo_lists/show.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.partial! "todo_lists/todo_list", todo_list: @todo_list
2 |
--------------------------------------------------------------------------------
/integration/todo/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/integration/todo/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path('../spring', __FILE__)
4 | rescue LoadError => e
5 | raise unless e.message.include?('spring')
6 | end
7 | APP_PATH = File.expand_path('../config/application', __dir__)
8 | require_relative '../config/boot'
9 | require 'rails/commands'
10 |
--------------------------------------------------------------------------------
/integration/todo/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path('../spring', __FILE__)
4 | rescue LoadError => e
5 | raise unless e.message.include?('spring')
6 | end
7 | require_relative '../config/boot'
8 | require 'rake'
9 | Rake.application.run
10 |
--------------------------------------------------------------------------------
/integration/todo/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'pathname'
3 | require 'fileutils'
4 | include FileUtils
5 |
6 | # path to your application root.
7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
8 |
9 | def system!(*args)
10 | system(*args) || abort("\n== Command #{args} failed ==")
11 | end
12 |
13 | chdir APP_ROOT do
14 | # This script is a starting point to setup your application.
15 | # Add necessary setup steps to this file.
16 |
17 | puts '== Installing dependencies =='
18 | system! 'gem install bundler --conservative'
19 | system('bundle check') || system!('bundle install')
20 |
21 | # Install JavaScript dependencies if using Yarn
22 | # system('bin/yarn')
23 |
24 |
25 | # puts "\n== Copying sample files =="
26 | # unless File.exist?('config/database.yml')
27 | # cp 'config/database.yml.sample', 'config/database.yml'
28 | # end
29 |
30 | puts "\n== Preparing database =="
31 | system! 'bin/rails db:setup'
32 |
33 | puts "\n== Removing old logs and tempfiles =="
34 | system! 'bin/rails log:clear tmp:clear'
35 |
36 | puts "\n== Restarting application server =="
37 | system! 'bin/rails restart'
38 | end
39 |
--------------------------------------------------------------------------------
/integration/todo/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This file loads spring without using Bundler, in order to be fast.
4 | # It gets overwritten when you run the `spring binstub` command.
5 |
6 | unless defined?(Spring)
7 | require 'rubygems'
8 | require 'bundler'
9 |
10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
11 | spring = lockfile.specs.detect { |spec| spec.name == "spring" }
12 | if spring
13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
14 | gem 'spring', spring.version
15 | require 'spring/binstub'
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/integration/todo/bin/update:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'pathname'
3 | require 'fileutils'
4 | include FileUtils
5 |
6 | # path to your application root.
7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
8 |
9 | def system!(*args)
10 | system(*args) || abort("\n== Command #{args} failed ==")
11 | end
12 |
13 | chdir APP_ROOT do
14 | # This script is a way to update your development environment automatically.
15 | # Add necessary update steps to this file.
16 |
17 | puts '== Installing dependencies =='
18 | system! 'gem install bundler --conservative'
19 | system('bundle check') || system!('bundle install')
20 |
21 | puts "\n== Updating database =="
22 | system! 'bin/rails db:migrate'
23 |
24 | puts "\n== Removing old logs and tempfiles =="
25 | system! 'bin/rails log:clear tmp:clear'
26 |
27 | puts "\n== Restarting application server =="
28 | system! 'bin/rails restart'
29 | end
30 |
--------------------------------------------------------------------------------
/integration/todo/bin/yarn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | VENDOR_PATH = File.expand_path('..', __dir__)
3 | Dir.chdir(VENDOR_PATH) do
4 | begin
5 | exec "yarnpkg #{ARGV.join(" ")}"
6 | rescue Errno::ENOENT
7 | $stderr.puts "Yarn executable was not detected in the system."
8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
9 | exit 1
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/integration/todo/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative 'config/environment'
4 |
5 | run Rails.application
6 |
--------------------------------------------------------------------------------
/integration/todo/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative 'boot'
2 |
3 | require 'rails/all'
4 |
5 | # Require the gems listed in Gemfile, including any gems
6 | # you've limited to :test, :development, or :production.
7 | Bundler.require(*Rails.groups)
8 |
9 | module Todo
10 | class Application < Rails::Application
11 | # Initialize configuration defaults for originally generated Rails version.
12 | config.load_defaults 5.1
13 |
14 | # Settings in config/environments/* take precedence over those specified here.
15 | # Application configuration should go into files in config/initializers
16 | # -- all .rb files in that directory are automatically loaded.
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/integration/todo/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
2 |
3 | require 'bundler/setup' # Set up gems listed in the Gemfile.
4 |
--------------------------------------------------------------------------------
/integration/todo/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: async
6 |
7 | production:
8 | adapter: redis
9 | url: redis://localhost:6379/1
10 | channel_prefix: todo_production
11 |
--------------------------------------------------------------------------------
/integration/todo/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | #
7 | default: &default
8 | adapter: sqlite3
9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | database: db/development.sqlite3
15 |
16 | # Warning: The database defined as "test" will be erased and
17 | # re-generated from your development database when you run "rake".
18 | # Do not set this db to the same as development or production.
19 | test:
20 | <<: *default
21 | database: db/test.sqlite3
22 |
23 | production:
24 | <<: *default
25 | database: db/production.sqlite3
26 |
--------------------------------------------------------------------------------
/integration/todo/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative 'application'
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/integration/todo/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports.
13 | config.consider_all_requests_local = true
14 |
15 | # Enable/disable caching. By default caching is disabled.
16 | if Rails.root.join('tmp/caching-dev.txt').exist?
17 | config.action_controller.perform_caching = true
18 |
19 | config.cache_store = :memory_store
20 | config.public_file_server.headers = {
21 | 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}"
22 | }
23 | else
24 | config.action_controller.perform_caching = false
25 |
26 | config.cache_store = :null_store
27 | end
28 |
29 | # Don't care if the mailer can't send.
30 | config.action_mailer.raise_delivery_errors = false
31 |
32 | config.action_mailer.perform_caching = false
33 |
34 | # Print deprecation notices to the Rails logger.
35 | config.active_support.deprecation = :log
36 |
37 | # Raise an error on page load if there are pending migrations.
38 | config.active_record.migration_error = :page_load
39 |
40 | # Debug mode disables concatenation and preprocessing of assets.
41 | # This option may cause significant delays in view rendering with a large
42 | # number of complex assets.
43 | config.assets.debug = true
44 |
45 | # Suppress logger output for asset requests.
46 | config.assets.quiet = true
47 |
48 | # Raises error for missing translations
49 | # config.action_view.raise_on_missing_translations = true
50 |
51 | # Use an evented file watcher to asynchronously detect changes in source code,
52 | # routes, locales, etc. This feature depends on the listen gem.
53 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker
54 | end
55 |
--------------------------------------------------------------------------------
/integration/todo/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Attempt to read encrypted secrets from `config/secrets.yml.enc`.
18 | # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or
19 | # `config/secrets.yml.key`.
20 | config.read_encrypted_secrets = true
21 |
22 | # Disable serving static files from the `/public` folder by default since
23 | # Apache or NGINX already handles this.
24 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
25 |
26 | # Compress JavaScripts and CSS.
27 | config.assets.js_compressor = :uglifier
28 | # config.assets.css_compressor = :sass
29 |
30 | # Do not fallback to assets pipeline if a precompiled asset is missed.
31 | config.assets.compile = false
32 |
33 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
34 |
35 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
36 | # config.action_controller.asset_host = 'http://assets.example.com'
37 |
38 | # Specifies the header that your server uses for sending files.
39 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
40 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
41 |
42 | # Mount Action Cable outside main process or domain
43 | # config.action_cable.mount_path = nil
44 | # config.action_cable.url = 'wss://example.com/cable'
45 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
46 |
47 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
48 | # config.force_ssl = true
49 |
50 | # Use the lowest log level to ensure availability of diagnostic information
51 | # when problems arise.
52 | config.log_level = :debug
53 |
54 | # Prepend all log lines with the following tags.
55 | config.log_tags = [ :request_id ]
56 |
57 | # Use a different cache store in production.
58 | # config.cache_store = :mem_cache_store
59 |
60 | # Use a real queuing backend for Active Job (and separate queues per environment)
61 | # config.active_job.queue_adapter = :resque
62 | # config.active_job.queue_name_prefix = "todo_#{Rails.env}"
63 | config.action_mailer.perform_caching = false
64 |
65 | # Ignore bad email addresses and do not raise email delivery errors.
66 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
67 | # config.action_mailer.raise_delivery_errors = false
68 |
69 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
70 | # the I18n.default_locale when a translation cannot be found).
71 | config.i18n.fallbacks = true
72 |
73 | # Send deprecation notices to registered listeners.
74 | config.active_support.deprecation = :notify
75 |
76 | # Use default logging formatter so that PID and timestamp are not suppressed.
77 | config.log_formatter = ::Logger::Formatter.new
78 |
79 | # Use a different logger for distributed setups.
80 | # require 'syslog/logger'
81 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
82 |
83 | if ENV["RAILS_LOG_TO_STDOUT"].present?
84 | logger = ActiveSupport::Logger.new(STDOUT)
85 | logger.formatter = config.log_formatter
86 | config.logger = ActiveSupport::TaggedLogging.new(logger)
87 | end
88 |
89 | # Do not dump schema after migrations.
90 | config.active_record.dump_schema_after_migration = false
91 | end
92 |
--------------------------------------------------------------------------------
/integration/todo/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure public file server for tests with Cache-Control for performance.
16 | config.public_file_server.enabled = true
17 | config.public_file_server.headers = {
18 | 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}"
19 | }
20 |
21 | # Show full error reports and disable caching.
22 | config.consider_all_requests_local = true
23 | config.action_controller.perform_caching = false
24 |
25 | # Raise exceptions instead of rendering exception templates.
26 | config.action_dispatch.show_exceptions = false
27 |
28 | # Disable request forgery protection in test environment.
29 | config.action_controller.allow_forgery_protection = false
30 | config.action_mailer.perform_caching = false
31 |
32 | # Tell Action Mailer not to deliver emails to the real world.
33 | # The :test delivery method accumulates sent emails in the
34 | # ActionMailer::Base.deliveries array.
35 | config.action_mailer.delivery_method = :test
36 |
37 | # Print deprecation notices to the stderr.
38 | config.active_support.deprecation = :stderr
39 |
40 | # Raises error for missing translations
41 | # config.action_view.raise_on_missing_translations = true
42 | end
43 |
--------------------------------------------------------------------------------
/integration/todo/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ApplicationController.renderer.defaults.merge!(
4 | # http_host: 'example.org',
5 | # https: false
6 | # )
7 |
--------------------------------------------------------------------------------
/integration/todo/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path.
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 | # Add Yarn node_modules folder to the asset load path.
9 | Rails.application.config.assets.paths << Rails.root.join('node_modules')
10 |
11 | # Precompile additional assets.
12 | # application.js, application.css, and all non-JS/CSS in the app/assets
13 | # folder are already added.
14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css )
15 |
--------------------------------------------------------------------------------
/integration/todo/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/integration/todo/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Specify a serializer for the signed and encrypted cookie jars.
4 | # Valid options are :json, :marshal, and :hybrid.
5 | Rails.application.config.action_dispatch.cookies_serializer = :json
6 |
--------------------------------------------------------------------------------
/integration/todo/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
5 |
--------------------------------------------------------------------------------
/integration/todo/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/integration/todo/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/integration/todo/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/integration/todo/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at http://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/integration/todo/config/puma.rb:
--------------------------------------------------------------------------------
1 | # Puma can serve each request in a thread from an internal thread pool.
2 | # The `threads` method setting takes two numbers: a minimum and maximum.
3 | # Any libraries that use thread pools should be configured to match
4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum
5 | # and maximum; this matches the default thread size of Active Record.
6 | #
7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
8 | threads threads_count, threads_count
9 |
10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
11 | #
12 | port ENV.fetch("PORT") { 3000 }
13 |
14 | # Specifies the `environment` that Puma will run in.
15 | #
16 | environment ENV.fetch("RAILS_ENV") { "development" }
17 |
18 | # Specifies the number of `workers` to boot in clustered mode.
19 | # Workers are forked webserver processes. If using threads and workers together
20 | # the concurrency of the application would be max `threads` * `workers`.
21 | # Workers do not work on JRuby or Windows (both of which do not support
22 | # processes).
23 | #
24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
25 |
26 | # Use the `preload_app!` method when specifying a `workers` number.
27 | # This directive tells Puma to first boot the application and load code
28 | # before forking the application. This takes advantage of Copy On Write
29 | # process behavior so workers use less memory. If you use this option
30 | # you need to make sure to reconnect any threads in the `on_worker_boot`
31 | # block.
32 | #
33 | # preload_app!
34 |
35 | # If you are preloading your application and using Active Record, it's
36 | # recommended that you close any connections to the database before workers
37 | # are forked to prevent connection leakage.
38 | #
39 | # before_fork do
40 | # ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord)
41 | # end
42 |
43 | # The code in the `on_worker_boot` will be called if you are using
44 | # clustered mode by specifying a number of `workers`. After each worker
45 | # process is booted, this block will be run. If you are using the `preload_app!`
46 | # option, you will want to use this block to reconnect to any threads
47 | # or connections that may have been created at application boot, as Ruby
48 | # cannot share connections between processes.
49 | #
50 | # on_worker_boot do
51 | # ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
52 | # end
53 | #
54 |
55 | # Allow puma to be restarted by `rails restart` command.
56 | plugin :tmp_restart
57 |
--------------------------------------------------------------------------------
/integration/todo/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | resources :todo_lists do
3 | resources :todo_items do
4 | member do
5 | patch :complete
6 | end
7 | end
8 | end
9 |
10 | root "todo_lists#index"
11 | end
12 |
--------------------------------------------------------------------------------
/integration/todo/config/secrets.yml:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Your secret key is used for verifying the integrity of signed cookies.
4 | # If you change this key, all old signed cookies will become invalid!
5 |
6 | # Make sure the secret is at least 30 characters and all random,
7 | # no regular words or you'll be exposed to dictionary attacks.
8 | # You can use `rails secret` to generate a secure secret key.
9 |
10 | # Make sure the secrets in this file are kept private
11 | # if you're sharing your code publicly.
12 |
13 | # Shared secrets are available across all environments.
14 |
15 | # shared:
16 | # api_key: a1B2c3D4e5F6
17 |
18 | # Environmental secrets are only available for that specific environment.
19 |
20 | development:
21 | secret_key_base: 93e0f81d8e9dcf955f7480867ceb5a969f09837e83a61d85de4ef4c32b3a84c87516dcde79e4c9442f75c0d7c2b6fc259f34e90118fe456570ee94acc4791881
22 |
23 | test:
24 | secret_key_base: fa4aa30ead1cbd541294dc4a492f4c409fa2d52e2f1937634a817dcbc68f4bf3559cfe80a0344d27f99d047d368f309fb8f092ec91b692cf49ee61338c216b3c
25 |
26 | # Do not keep production secrets in the unencrypted secrets file.
27 | # Instead, either read values from the environment.
28 | # Or, use `bin/rails secrets:setup` to configure encrypted secrets
29 | # and move the `production:` environment over there.
30 |
31 | production:
32 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
33 |
--------------------------------------------------------------------------------
/integration/todo/config/spring.rb:
--------------------------------------------------------------------------------
1 | %w(
2 | .ruby-version
3 | .rbenv-vars
4 | tmp/restart.txt
5 | tmp/caching-dev.txt
6 | ).each { |path| Spring.watch(path) }
7 |
--------------------------------------------------------------------------------
/integration/todo/db/migrate/20180818210306_create_todo_lists.rb:
--------------------------------------------------------------------------------
1 | class CreateTodoLists < ActiveRecord::Migration[5.1]
2 | def change
3 | create_table :todo_lists do |t|
4 | t.string :title
5 | t.text :description
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/integration/todo/db/migrate/20180819121311_create_todo_items.rb:
--------------------------------------------------------------------------------
1 | class CreateTodoItems < ActiveRecord::Migration[5.1]
2 | def change
3 | create_table :todo_items do |t|
4 | t.string :content
5 | t.references :todo_list, foreign_key: true
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/integration/todo/db/migrate/20180819123754_add_completed_at_to_todo_items.rb:
--------------------------------------------------------------------------------
1 | class AddCompletedAtToTodoItems < ActiveRecord::Migration[5.1]
2 | def change
3 | add_column :todo_items, :completed_at, :datetime
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/integration/todo/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # Note that this schema.rb definition is the authoritative source for your
6 | # database schema. If you need to create the application database on another
7 | # system, you should be using db:schema:load, not running all the migrations
8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 | # you'll amass, the slower it'll run and the greater likelihood for issues).
10 | #
11 | # It's strongly recommended that you check this file into your version control system.
12 |
13 | ActiveRecord::Schema.define(version: 20180819123754) do
14 |
15 | create_table "todo_items", force: :cascade do |t|
16 | t.string "content"
17 | t.integer "todo_list_id"
18 | t.datetime "created_at", null: false
19 | t.datetime "updated_at", null: false
20 | t.datetime "completed_at"
21 | t.index ["todo_list_id"], name: "index_todo_items_on_todo_list_id"
22 | end
23 |
24 | create_table "todo_lists", force: :cascade do |t|
25 | t.string "title"
26 | t.text "description"
27 | t.datetime "created_at", null: false
28 | t.datetime "updated_at", null: false
29 | end
30 |
31 | end
32 |
--------------------------------------------------------------------------------
/integration/todo/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
7 | # Character.create(name: 'Luke', movie: movies.first)
8 |
--------------------------------------------------------------------------------
/integration/todo/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/lib/assets/.keep
--------------------------------------------------------------------------------
/integration/todo/lib/tasks/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/lib/tasks/.keep
--------------------------------------------------------------------------------
/integration/todo/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/log/.keep
--------------------------------------------------------------------------------
/integration/todo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "todo",
3 | "private": true,
4 | "dependencies": {}
5 | }
6 |
--------------------------------------------------------------------------------
/integration/todo/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/integration/todo/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/integration/todo/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/integration/todo/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/public/apple-touch-icon-precomposed.png
--------------------------------------------------------------------------------
/integration/todo/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/integration/todo/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/public/favicon.ico
--------------------------------------------------------------------------------
/integration/todo/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 |
--------------------------------------------------------------------------------
/integration/todo/sonar-project.properties:
--------------------------------------------------------------------------------
1 | sonar.projectKey=fortititudetec:todo
2 | sonar.projectName=ToDo Application
3 | sonar.projectVersion=1.0
4 | sonar.sourceEncoding=UTF-8
5 | sonar.sources=app
6 | sonar.exclusions=**/node_modules/**,**/*.spec.ts
7 | sonar.tests=test
8 | sonar.test.inclusions=**/*.spec.ts
9 |
10 | sonar.ruby.file.suffixes=rb,ruby
11 | sonar.ruby.coverage.reportPath=coverage/.resultset.json
12 | sonar.ruby.coverage.framework=MiniTest
13 | sonar.ruby.rubocopConfig=.rubocop.yml
14 | sonar.ruby.rubocop=/usr/bin/rubocop
15 | sonar.ruby.rubocop.reportPath=rubocop-result.json
16 | sonar.ruby.rubocop.filePath=.
17 |
--------------------------------------------------------------------------------
/integration/todo/test/application_system_test_case.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
5 | end
6 |
--------------------------------------------------------------------------------
/integration/todo/test/controllers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/test/controllers/.keep
--------------------------------------------------------------------------------
/integration/todo/test/controllers/todo_items_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class TodoItemsControllerTest < ActionDispatch::IntegrationTest
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/integration/todo/test/controllers/todo_lists_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class TodoListsControllerTest < ActionDispatch::IntegrationTest
4 | setup do
5 | @todo_list = todo_lists(:one)
6 | end
7 |
8 | test "should get index" do
9 | get todo_lists_url
10 | assert_response :success
11 | end
12 |
13 | test "should get new" do
14 | get new_todo_list_url
15 | assert_response :success
16 | end
17 |
18 | test "should create todo_list" do
19 | assert_difference('TodoList.count') do
20 | post todo_lists_url, params: { todo_list: { description: @todo_list.description, title: @todo_list.title } }
21 | end
22 |
23 | assert_redirected_to todo_list_url(TodoList.last)
24 | end
25 |
26 | test "should show todo_list" do
27 | get todo_list_url(@todo_list)
28 | assert_response :success
29 | end
30 |
31 | test "should get edit" do
32 | get edit_todo_list_url(@todo_list)
33 | assert_response :success
34 | end
35 |
36 | test "should update todo_list" do
37 | patch todo_list_url(@todo_list), params: { todo_list: { description: @todo_list.description, title: @todo_list.title } }
38 | assert_redirected_to todo_list_url(@todo_list)
39 | end
40 |
41 | test "should destroy todo_list" do
42 | assert_difference('TodoList.count', -1) do
43 | delete todo_list_url(@todo_list)
44 | end
45 |
46 | assert_redirected_to todo_lists_url
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/integration/todo/test/fixtures/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/test/fixtures/.keep
--------------------------------------------------------------------------------
/integration/todo/test/fixtures/files/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/test/fixtures/files/.keep
--------------------------------------------------------------------------------
/integration/todo/test/fixtures/todo_items.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | one:
4 | content: MyString
5 | todo_list: one
6 |
7 | two:
8 | content: MyString
9 | todo_list: two
10 |
--------------------------------------------------------------------------------
/integration/todo/test/fixtures/todo_lists.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | one:
4 | title: MyString
5 | description: MyText
6 |
7 | two:
8 | title: MyString
9 | description: MyText
10 |
--------------------------------------------------------------------------------
/integration/todo/test/helpers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/test/helpers/.keep
--------------------------------------------------------------------------------
/integration/todo/test/integration/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/test/integration/.keep
--------------------------------------------------------------------------------
/integration/todo/test/mailers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/test/mailers/.keep
--------------------------------------------------------------------------------
/integration/todo/test/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/test/models/.keep
--------------------------------------------------------------------------------
/integration/todo/test/models/todo_item_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class TodoItemTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/integration/todo/test/models/todo_list_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class TodoListTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/integration/todo/test/system/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/test/system/.keep
--------------------------------------------------------------------------------
/integration/todo/test/system/todo_lists_test.rb:
--------------------------------------------------------------------------------
1 | require "application_system_test_case"
2 |
3 | class TodoListsTest < ApplicationSystemTestCase
4 | # test "visiting the index" do
5 | # visit todo_lists_url
6 | #
7 | # assert_selector "h1", text: "TodoList"
8 | # end
9 | end
10 |
--------------------------------------------------------------------------------
/integration/todo/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | require 'simplecov'
2 | SimpleCov.start
3 |
4 | require File.expand_path('../../config/environment', __FILE__)
5 | require 'rails/test_help'
6 |
7 | Minitest::Reporters.use!
8 |
9 | class ActiveSupport::TestCase
10 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
11 | fixtures :all
12 |
13 | # Add more helper methods to be used by all tests here...
14 | end
15 |
--------------------------------------------------------------------------------
/integration/todo/tmp/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/tmp/.keep
--------------------------------------------------------------------------------
/integration/todo/vendor/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fortitudetec/sonar-ruby-plugin/3d82c28e5e46fe2a5b9cb6149877b79a3a969a8f/integration/todo/vendor/.keep
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.fortitudetec.sonar
8 | sonar-ruby-plugin
9 | 1.1.0
10 | sonar-plugin
11 |
12 | Sonar Ruby Plugin
13 | Analyze Ruby Source for code quality and code test coverage
14 | 2017
15 | https://github.com/fortitudetec/sonar-ruby-plugin
16 |
17 |
18 | Fortitude Technologies
19 | https://fortitudetec.com
20 |
21 |
22 |
23 |
24 | chrisrohr
25 | Chris Rohr
26 |
27 |
28 |
29 |
30 |
31 | MIT License
32 | http://www.opensource.org/licenses/mit-license.php
33 |
34 |
35 |
36 |
37 | https://@github.com/fortitudetec/sonar-ruby-plugin
38 | scm:git:git@github.com:fortitudetec/sonar-ruby-plugin.git
39 |
40 |
41 |
42 | Github
43 | https://github.com/fortitudetec/sonar-ruby-plugin/issues
44 |
45 |
46 |
47 | Travis
48 | https://travis-ci.org/fortitudetec/sonar-ruby-plugin
49 |
50 |
51 |
52 | UTF-8
53 |
54 | 5.6
55 | 1.8
56 | 1.21
57 |
58 | target/surefire-reports
59 | target/jacoco.exec
60 |
61 |
62 |
63 |
64 | org.sonarsource.sonarqube
65 | sonar-plugin-api
66 | ${sonar.buildVersion}
67 | provided
68 |
69 |
70 |
71 | org.sonarsource.sslr-squid-bridge
72 | sslr-squid-bridge
73 | 2.6.1
74 |
75 |
76 |
77 | org.yaml
78 | snakeyaml
79 | 1.17
80 |
81 |
82 |
83 | org.slf4j
84 | slf4j-api
85 | 1.7.24
86 |
87 |
88 |
89 | org.apache.commons
90 | commons-lang3
91 | 3.5
92 |
93 |
94 |
95 | com.google.guava
96 | guava
97 | 21.0
98 |
99 |
100 |
101 | com.fasterxml.jackson.core
102 | jackson-core
103 | 2.8.7
104 |
105 |
106 |
107 | com.fasterxml.jackson.core
108 | jackson-annotations
109 | 2.8.0
110 |
111 |
112 |
113 | com.fasterxml.jackson.core
114 | jackson-databind
115 | 2.8.7
116 |
117 |
118 |
119 | org.projectlombok
120 | lombok
121 | 1.16.6
122 |
123 |
124 |
125 | junit
126 | junit
127 | 4.12
128 | test
129 |
130 |
131 | org.assertj
132 | assertj-core
133 | 3.8.0
134 | test
135 |
136 |
137 | org.mockito
138 | mockito-core
139 | 1.10.19
140 | test
141 |
142 |
143 |
144 |
145 |
146 |
147 | org.sonarsource.sonar-packaging-maven-plugin
148 | sonar-packaging-maven-plugin
149 | 1.16
150 | true
151 |
152 | ruby
153 | com.fortitudetec.sonar.plugins.ruby.RubyPlugin
154 | Ruby
155 |
156 |
157 |
158 | org.apache.maven.plugins
159 | maven-compiler-plugin
160 | 2.5.1
161 |
162 | ${jdk.min.version}
163 | ${jdk.min.version}
164 |
165 | true
166 |
167 |
168 |
169 |
170 | maven-shade-plugin
171 |
172 |
173 | package
174 |
175 | shade
176 |
177 |
178 | false
179 | true
180 | false
181 |
182 |
183 | cglib:cglib-nodep
184 |
185 | **
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 | org.sonarsource.scanner.maven
196 | sonar-maven-plugin
197 | 3.2
198 |
199 |
200 |
201 |
202 |
--------------------------------------------------------------------------------
/sonar-project.properties:
--------------------------------------------------------------------------------
1 | sonar.projectKey = sonarqube-ruby-plugin
2 | sonar.sources = .
3 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/PathResolver.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.sonar.api.batch.BatchSide;
6 | import org.sonar.api.batch.sensor.SensorContext;
7 |
8 | import java.io.File;
9 |
10 | @BatchSide
11 | public class PathResolver {
12 | private static final Logger LOG = LoggerFactory.getLogger(PathResolver.class);
13 |
14 | public String getPath(SensorContext context, String settingKey, String defaultValue) {
15 | // Prefer the specified path
16 | String toReturn = context.settings().getString(settingKey);
17 |
18 | // Fall back to a file system search if null or doesn't exist
19 | if (toReturn == null || toReturn.isEmpty()) {
20 | LOG.debug("Path {} not specified, falling back to {}", settingKey, defaultValue);
21 | toReturn = defaultValue;
22 | }
23 | else {
24 | LOG.debug("Found {} path to be '{}'", settingKey, toReturn);
25 | }
26 |
27 | return getAbsolutePath(context, toReturn);
28 | }
29 |
30 | private String getAbsolutePath(SensorContext context, String toReturn) {
31 | if (toReturn != null) {
32 | File candidateFile = new java.io.File(toReturn);
33 | if (!candidateFile.isAbsolute()) {
34 | candidateFile = new java.io.File(context.fileSystem().baseDir().getAbsolutePath(), toReturn);
35 | }
36 |
37 | if (!doesFileExist(candidateFile)) {
38 | return null;
39 | }
40 |
41 | return candidateFile.getAbsolutePath();
42 | }
43 |
44 | return null;
45 | }
46 |
47 | private boolean doesFileExist(File f) {
48 | return f.exists();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/Ruby.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby;
2 |
3 | import com.google.common.collect.Lists;
4 | import org.apache.commons.lang3.StringUtils;
5 | import org.sonar.api.config.Settings;
6 | import org.sonar.api.resources.AbstractLanguage;
7 |
8 | import java.util.List;
9 |
10 | public class Ruby extends AbstractLanguage {
11 |
12 | public static final String LANGUAGE_KEY = "rb";
13 |
14 | private static final String[] DEFAULT_FILE_SUFFIXES = { "rb", "Gemfile", "gemspec", "rake", "spec", "Capfile", "ru", "Rakefile" };
15 |
16 | public static final String[] RUBY_KEYWORDS_ARRAY =
17 | {
18 | "alias", "and", "BEGIN", "begin", "break", "case", "class", "def", "defined?",
19 | "do", "else", "elsif", "END", "end", "ensure", "false", "for", "if", "in", "module",
20 | "next", "nil", "not", "or", "redo", "rescue", "retry", "return", "self", "super",
21 | "then", "true", "undef", "unless", "until", "when", "while", "yield"
22 | };
23 |
24 | private Settings settings;
25 |
26 | public Ruby(Settings settings) {
27 | super(LANGUAGE_KEY, "Ruby");
28 | this.settings = settings;
29 | }
30 |
31 | @Override
32 | public String[] getFileSuffixes() {
33 | String[] suffixes = filterEmptyStrings(settings.getStringArray("sonar.ruby.file.suffixes"));
34 | return suffixes.length == 0 ? Ruby.DEFAULT_FILE_SUFFIXES : suffixes;
35 | }
36 |
37 | private static String[] filterEmptyStrings(String[] stringArray) {
38 | List nonEmptyStrings = Lists.newArrayList();
39 | for (String string : stringArray) {
40 | if (StringUtils.isNotBlank(string.trim())) {
41 | nonEmptyStrings.add(string.trim());
42 | }
43 | }
44 | return nonEmptyStrings.toArray(new String[0]);
45 | }
46 |
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/RubyPlugin.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby;
2 |
3 | import com.fortitudetec.sonar.plugins.ruby.metrics.CombinedCoverageSensor;
4 | import com.fortitudetec.sonar.plugins.ruby.metrics.RubyMetricsSensor;
5 | import com.fortitudetec.sonar.plugins.ruby.rubocop.RubocopExecutor;
6 | import com.fortitudetec.sonar.plugins.ruby.rubocop.RubocopParser;
7 | import com.fortitudetec.sonar.plugins.ruby.rubocop.RubocopSensor;
8 | import com.fortitudetec.sonar.plugins.ruby.simplecov.SimpleCovParser;
9 | import com.fortitudetec.sonar.plugins.ruby.simplecov.SimpleCovSensor;
10 | import org.sonar.api.Plugin;
11 | import org.sonar.api.config.PropertyDefinition;
12 | import org.sonar.api.resources.Qualifiers;
13 |
14 | public class RubyPlugin implements Plugin {
15 |
16 | private static final String RUBY_CATEGORY = "Ruby";
17 |
18 | // Subcategories
19 | private static final String GENERAL = "General";
20 | private static final String TEST_AND_COVERAGE = "Tests and Coverage";
21 | private static final String RUBOCOP = "Rubocop";
22 |
23 | // Properties
24 | public static final String FILE_SUFFIXES = "sonar.ruby.file.suffixes";
25 | public static final String SIMPLECOV_REPORT_PATH = "sonar.ruby.coverage.reportPath";
26 | public static final String TEST_FRAMEWORK = "sonar.ruby.coverage.framework";
27 | public static final String RUBOCOP_CONFIG = "sonar.ruby.rubocopConfig";
28 | public static final String RUBOCOP_BIN = "sonar.ruby.rubocop";
29 | public static final String RUBOCOP_REPORT_PATH = "sonar.ruby.rubocop.reportPath";
30 | public static final String RUBOCOP_FILES_PATH = "sonar.ruby.rubocop.filePath";
31 |
32 | public void define(Context context) {
33 |
34 | context.addExtensions(
35 | PropertyDefinition.builder(FILE_SUFFIXES)
36 | .name("File Suffixes")
37 | .description("Comma separated list of Ruby files to analyze.")
38 | .category(RUBY_CATEGORY)
39 | .subCategory(GENERAL)
40 | .onQualifiers(Qualifiers.PROJECT)
41 | .defaultValue("rb")
42 | .build(),
43 |
44 | // SimpleCov
45 | PropertyDefinition.builder(SIMPLECOV_REPORT_PATH)
46 | .name("Path to coverage reports")
47 | .description("Path to coverage reports.")
48 | .category(RUBY_CATEGORY)
49 | .subCategory(TEST_AND_COVERAGE)
50 | .onQualifiers(Qualifiers.PROJECT)
51 | .defaultValue("coverage/.resultset.json")
52 | .build(),
53 |
54 | PropertyDefinition.builder(TEST_FRAMEWORK)
55 | .name("Name of test framework")
56 | .description("Name of test framework.")
57 | .category(RUBY_CATEGORY)
58 | .subCategory(TEST_AND_COVERAGE)
59 | .onQualifiers(Qualifiers.PROJECT)
60 | .defaultValue("RSpec")
61 | .build(),
62 |
63 | // RUBOCOP
64 | PropertyDefinition.builder(RUBOCOP_CONFIG)
65 | .name("Rubocop configuration")
66 | .description("Path to the Rubocop configuration file to use in rubocop analysis. Set to empty to use the default.")
67 | .category(RUBY_CATEGORY)
68 | .subCategory(RUBOCOP)
69 | .onQualifiers(Qualifiers.PROJECT)
70 | .defaultValue(".rubocop.yml")
71 | .build(),
72 |
73 | PropertyDefinition.builder(RUBOCOP_BIN)
74 | .name("Rubocop executable")
75 | .description("Path to the rubocop executable to use in rubocop analysis. Set to empty to use the default one.")
76 | .category(RUBY_CATEGORY)
77 | .subCategory(RUBOCOP)
78 | .onQualifiers(Qualifiers.PROJECT)
79 | .defaultValue("")
80 | .build(),
81 |
82 | PropertyDefinition.builder(RUBOCOP_FILES_PATH)
83 | .name("Rubocop files to check")
84 | .description("The path to the files for rubocop to check")
85 | .category(RUBY_CATEGORY)
86 | .subCategory(RUBOCOP)
87 | .onQualifiers(Qualifiers.PROJECT)
88 | .defaultValue(".")
89 | .build(),
90 |
91 | PropertyDefinition.builder(RUBOCOP_REPORT_PATH)
92 | .name("Rubocop's reports")
93 | .description("Path to Rubocop's report file, relative to projects root")
94 | .category(RUBY_CATEGORY)
95 | .subCategory(RUBOCOP)
96 | .onQualifiers(Qualifiers.PROJECT)
97 | .defaultValue("rubocop-result.json")
98 | .build(),
99 |
100 | RubyRuleProfile.class,
101 | Ruby.class,
102 | RubyRulesDefinition.class,
103 | RubocopSensor.class,
104 |
105 | PathResolver.class,
106 | RubocopExecutor.class,
107 | RubocopParser.class,
108 | RubyMetricsSensor.class,
109 |
110 | SimpleCovSensor.class,
111 | SimpleCovParser.class,
112 | CombinedCoverageSensor.class
113 | );
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/RubyRuleProfile.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby;
2 |
3 | import com.fortitudetec.sonar.plugins.ruby.model.RubocopRule;
4 | import org.sonar.api.profiles.ProfileDefinition;
5 | import org.sonar.api.profiles.RulesProfile;
6 | import org.sonar.api.rules.Rule;
7 | import org.sonar.api.utils.ValidationMessages;
8 |
9 | public class RubyRuleProfile extends ProfileDefinition {
10 | @Override
11 | public RulesProfile createProfile(ValidationMessages validationMessages) {
12 | RulesProfile profile = RulesProfile.create("Rubocop", Ruby.LANGUAGE_KEY);
13 |
14 | RubyRulesDefinition rules = new RubyRulesDefinition();
15 |
16 | activateRule(profile, RubyRulesDefinition.RUBY_LINT_UNKNOWN_RULE.key);
17 |
18 | for (RubocopRule coreRule : rules.getCoreRules()) {
19 | activateRule(profile, coreRule.key);
20 | }
21 |
22 | return profile;
23 | }
24 |
25 | private static void activateRule(RulesProfile profile, String ruleKey) {
26 | profile.activateRule(Rule.create("rubocop", ruleKey), null);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/RubyRulesDefinition.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby;
2 |
3 | import static org.apache.commons.lang3.StringUtils.startsWith;
4 | import static org.sonar.api.rule.Severity.CRITICAL;
5 | import static org.sonar.api.rule.Severity.INFO;
6 | import static org.sonar.api.rule.Severity.MINOR;
7 | import static org.sonar.api.rules.RuleType.BUG;
8 | import static org.sonar.api.rules.RuleType.CODE_SMELL;
9 | import static org.sonar.api.rules.RuleType.VULNERABILITY;
10 |
11 | import com.fortitudetec.sonar.plugins.ruby.model.RubocopRule;
12 | import org.sonar.api.rule.RuleStatus;
13 | import org.sonar.api.rule.Severity;
14 | import org.sonar.api.rules.RuleType;
15 | import org.sonar.api.server.rule.RulesDefinition;
16 | import org.yaml.snakeyaml.Yaml;
17 |
18 | import java.io.InputStream;
19 | import java.util.ArrayList;
20 | import java.util.Comparator;
21 | import java.util.List;
22 | import java.util.Map;
23 |
24 | public class RubyRulesDefinition implements RulesDefinition {
25 |
26 | /** The SonarQube rule that will contain all unknown TsLint issues. */
27 | public static final RubocopRule RUBY_LINT_UNKNOWN_RULE = new RubocopRule(
28 | "rubocop-issue",
29 | Severity.MAJOR,
30 | "rubocop issues that are not yet known to the plugin",
31 | CODE_SMELL,
32 | "No description for Rubocop rule");
33 |
34 | private List rubylintCoreRules = new ArrayList<>();
35 |
36 | public RubyRulesDefinition() {
37 | loadCoreRules();
38 | }
39 |
40 | @Override
41 | public void define(Context context) {
42 | NewRepository repository =
43 | context
44 | .createRepository("rubocop", Ruby.LANGUAGE_KEY)
45 | .setName("Rubocop Analyzer");
46 |
47 | createRule(repository, RUBY_LINT_UNKNOWN_RULE);
48 |
49 | // add the Rubocop builtin core rules
50 | for (RubocopRule coreRule : rubylintCoreRules) {
51 | createRule(repository, coreRule);
52 | }
53 |
54 | repository.done();
55 | }
56 |
57 | public List getCoreRules() {
58 | return rubylintCoreRules;
59 | }
60 |
61 | private void createRule(NewRepository repository, RubocopRule rubocopRule) {
62 | repository
63 | .createRule(rubocopRule.key)
64 | .setName(rubocopRule.name)
65 | .setSeverity(rubocopRule.severity)
66 | .setHtmlDescription(rubocopRule.htmlDescription)
67 | .setStatus(RuleStatus.READY)
68 | .setType(rubocopRule.debtType);
69 | }
70 |
71 | @SuppressWarnings("unchecked")
72 | private void loadCoreRules() {
73 | InputStream coreRulesStream = RubyRulesDefinition.class.getResourceAsStream("/rubocop/rubocop.yml");
74 | Yaml yaml = new Yaml();
75 |
76 | Map> rules = (Map>) yaml.load(coreRulesStream);
77 |
78 | rules.forEach((rule, metadata) -> {
79 | if ((Boolean) metadata.get("Enabled")) {
80 | rubylintCoreRules.add(new RubocopRule(rule, findSeverity(rule), rule, findType(rule), (String) metadata.get("Description")));
81 | }
82 | });
83 |
84 | rubylintCoreRules.sort(Comparator.comparing(r -> r.key));
85 | }
86 |
87 | private static String findSeverity(String rule) {
88 | String severity = MINOR;
89 |
90 | if (startsWith(rule, "Metrics") || startsWith(rule, "Bundler")) {
91 | severity = INFO;
92 | } else if (startsWith(rule, "Security")) {
93 | severity = CRITICAL;
94 | }
95 |
96 | return severity;
97 | }
98 |
99 | private static RuleType findType(String rule) {
100 | RuleType type = CODE_SMELL;
101 |
102 | if (startsWith(rule, "Performance")) {
103 | type = BUG;
104 | } else if (startsWith(rule, "Security")) {
105 | type = VULNERABILITY;
106 | }
107 |
108 | return type;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/metrics/ClassCountParser.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.metrics;
2 |
3 | import org.apache.commons.io.FileUtils;
4 | import org.apache.commons.io.LineIterator;
5 | import org.apache.commons.lang.StringUtils;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | import java.io.File;
10 | import java.io.IOException;
11 |
12 | public class ClassCountParser {
13 | private static final Logger LOG = LoggerFactory.getLogger(ClassCountParser.class);
14 |
15 | private ClassCountParser() {}
16 |
17 | public static int countClasses(File file) {
18 | int numClasses = 0;
19 | LineIterator iterator = null;
20 | try {
21 | iterator = FileUtils.lineIterator(file);
22 |
23 | while (iterator.hasNext()) {
24 | String line = iterator.nextLine();
25 | if (StringUtils.contains(line.trim(), "class ")) {
26 | numClasses++;
27 | }
28 | }
29 | } catch (IOException e) {
30 | LOG.error("Error determining class count for file " + file, e);
31 | } finally {
32 | LineIterator.closeQuietly(iterator);
33 | }
34 |
35 | return numClasses;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/metrics/CombinedCoverageSensor.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.metrics;
2 |
3 | import com.fortitudetec.sonar.plugins.ruby.Ruby;
4 | import com.fortitudetec.sonar.plugins.ruby.simplecov.SimpleCovSensor;
5 | import org.sonar.api.batch.sensor.Sensor;
6 | import org.sonar.api.batch.sensor.SensorContext;
7 | import org.sonar.api.batch.sensor.SensorDescriptor;
8 |
9 | public class CombinedCoverageSensor implements Sensor {
10 |
11 | private RubyMetricsSensor locSensor;
12 | private SimpleCovSensor coverageSensor;
13 |
14 | public CombinedCoverageSensor(RubyMetricsSensor locSensor, SimpleCovSensor coverageSensor) {
15 | this.locSensor = locSensor;
16 | this.coverageSensor = coverageSensor;
17 | }
18 |
19 | @Override
20 | public void describe(SensorDescriptor descriptor) {
21 | descriptor.name("Combined SimpleCov and LOC sensor");
22 | descriptor.onlyOnLanguage(Ruby.LANGUAGE_KEY);
23 | }
24 |
25 | @Override
26 | public void execute(SensorContext context) {
27 | locSensor.execute(context);
28 | coverageSensor.execute(context);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/metrics/CommentCountParser.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.metrics;
2 |
3 | import org.apache.commons.io.FileUtils;
4 | import org.apache.commons.io.LineIterator;
5 | import org.apache.commons.lang.StringUtils;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | import java.io.File;
10 | import java.io.IOException;
11 |
12 | public class CommentCountParser {
13 | private static final Logger LOG = LoggerFactory.getLogger(CommentCountParser.class);
14 |
15 | private CommentCountParser() {}
16 |
17 | public static int countLinesOfComment(File file) {
18 | int numComments = 0;
19 | LineIterator iterator = null;
20 | try {
21 | iterator = FileUtils.lineIterator(file);
22 |
23 | while (iterator.hasNext()) {
24 | String line = iterator.nextLine();
25 | if (StringUtils.startsWith(line.trim(), "#")) {
26 | numComments++;
27 | }
28 | }
29 | } catch (IOException e) {
30 | LOG.error("Error determining comment count for file " + file, e);
31 | } finally {
32 | LineIterator.closeQuietly(iterator);
33 | }
34 |
35 | return numComments;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/metrics/RubyMetricsSensor.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.metrics;
2 |
3 | import com.fortitudetec.sonar.plugins.ruby.Ruby;
4 | import com.fortitudetec.sonar.plugins.ruby.squid.RubyRecognizer;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.sonar.api.batch.BatchSide;
8 | import org.sonar.api.batch.fs.InputFile;
9 | import org.sonar.api.batch.sensor.SensorContext;
10 | import org.sonar.api.measures.CoreMetrics;
11 | import org.sonar.squidbridge.measures.Metric;
12 | import org.sonar.squidbridge.text.Source;
13 |
14 | import java.io.BufferedReader;
15 | import java.io.FileReader;
16 | import java.io.IOException;
17 |
18 | @BatchSide
19 | public class RubyMetricsSensor {
20 | private static final Logger LOG = LoggerFactory.getLogger(RubyMetricsSensor.class);
21 |
22 | public void execute(SensorContext ctx) {
23 | Iterable affectedFiles = gatherFiles(ctx);
24 | affectedFiles.forEach(inputFile -> setMetricsForFile(inputFile, ctx));
25 | }
26 |
27 | private void setMetricsForFile(InputFile inputFile, SensorContext ctx) {
28 | try (FileReader fileReader = new FileReader(inputFile.file()); BufferedReader reader = new BufferedReader(fileReader)) {
29 | Source source = new Source(reader, new RubyRecognizer(), "#");
30 |
31 | ctx.newMeasure().forMetric(CoreMetrics.NCLOC).on(inputFile).withValue(source.getMeasure(Metric.LINES_OF_CODE)).save();
32 | ctx.newMeasure().forMetric(CoreMetrics.COMMENT_LINES).on(inputFile).withValue(CommentCountParser.countLinesOfComment(inputFile.file())).save();
33 | ctx.newMeasure().forMetric(CoreMetrics.CLASSES).on(inputFile).withValue(ClassCountParser.countClasses(inputFile.file())).save();
34 |
35 | } catch (IOException e) {
36 | LOG.warn("Unable to read ruby file to gather metrics.", e);
37 | }
38 | }
39 |
40 | private Iterable gatherFiles(SensorContext ctx) {
41 | return ctx
42 | .fileSystem()
43 | .inputFiles(ctx.fileSystem().predicates().hasLanguage(Ruby.LANGUAGE_KEY));
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/model/RubocopIssue.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.model;
2 |
3 | import lombok.Builder;
4 | import lombok.Getter;
5 | import lombok.Setter;
6 | import lombok.ToString;
7 |
8 | @Getter
9 | @Setter
10 | @ToString
11 | @Builder
12 | public class RubocopIssue {
13 | private RubocopPosition position;
14 | private String failure;
15 | private String ruleName;
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/model/RubocopPosition.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.model;
2 |
3 | import lombok.Builder;
4 | import lombok.Getter;
5 | import lombok.Setter;
6 | import lombok.ToString;
7 |
8 | @Getter
9 | @Setter
10 | @ToString
11 | @Builder
12 | public class RubocopPosition {
13 | private int line;
14 | private int character;
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/model/RubocopRule.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.model;
2 |
3 | import org.sonar.api.rules.RuleType;
4 |
5 | public class RubocopRule {
6 | public final String key;
7 | public final String name;
8 | public final String severity;
9 | public final String htmlDescription;
10 | public final RuleType debtType;
11 |
12 | public RubocopRule(String key,String severity, String name, RuleType debtType, String htmlDescription) {
13 | this.key = key;
14 | this.severity = severity;
15 | this.name = name;
16 | this.debtType = debtType;
17 | this.htmlDescription = htmlDescription;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/rubocop/RubocopExecutor.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.rubocop;
2 |
3 | import static com.google.common.base.Preconditions.checkNotNull;
4 |
5 | import com.google.common.base.Charsets;
6 | import com.google.common.io.Files;
7 | import org.apache.commons.lang3.StringUtils;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 | import org.sonar.api.batch.BatchSide;
11 | import org.sonar.api.utils.System2;
12 | import org.sonar.api.utils.TempFolder;
13 | import org.sonar.api.utils.command.Command;
14 | import org.sonar.api.utils.command.CommandExecutor;
15 | import org.sonar.api.utils.command.StreamConsumer;
16 | import org.sonar.api.utils.command.StringStreamConsumer;
17 |
18 | import java.io.File;
19 | import java.io.IOException;
20 | import java.util.List;
21 |
22 | @BatchSide
23 | public class RubocopExecutor {
24 |
25 | private static final Logger LOG = LoggerFactory.getLogger(RubocopExecutor.class);
26 |
27 | private boolean mustQuoteSpaceContainingPaths;
28 | private TempFolder tempFolder;
29 |
30 | public RubocopExecutor(System2 system, TempFolder tempFolder) {
31 | this.mustQuoteSpaceContainingPaths = system.isOsWindows();
32 | this.tempFolder = tempFolder;
33 | }
34 |
35 | public String execute(RubocopExecutorConfig config, List files) {
36 | checkNotNull(config, "RubcopExecutorConfig must not be null");
37 | checkNotNull(files, "List of files must not be null");
38 |
39 | if (config.useExistingRubocopOutput()) {
40 | LOG.debug("Running with existing JSON file '{}' instead of calling rubocop", config.getPathToRubocopOutput());
41 | return getFileContent(new File(config.getPathToRubocopOutput()));
42 | }
43 |
44 | File rubocopOutputFile = tempFolder.newFile();
45 | String rubocopOutputFilePath = rubocopOutputFile.getAbsolutePath();
46 | Command baseCommand = getBaseCommand(config, rubocopOutputFilePath);
47 |
48 | StringStreamConsumer stdOutConsumer = new StringStreamConsumer();
49 | StringStreamConsumer stdErrConsumer = new StringStreamConsumer();
50 | return getCommandOutput(baseCommand, stdOutConsumer, stdErrConsumer, rubocopOutputFile, config.getTimeoutMs());
51 | }
52 |
53 | private String getFileContent(File rubocopOutputFile) {
54 | try {
55 | return Files.toString(rubocopOutputFile, Charsets.UTF_8);
56 | } catch (IOException e) {
57 | LOG.error("Failed to read Rubocop output", e);
58 | }
59 |
60 | return "";
61 | }
62 |
63 | private Command getBaseCommand(RubocopExecutorConfig config, String tempPath) {
64 | Command command =
65 | Command
66 | .create(config.getPathToRubocop())
67 | .addArgument(preparePath(config.getPathToRubocop()))
68 | .addArgument("--format")
69 | .addArgument("json");
70 |
71 | if (StringUtils.isNotBlank(tempPath)) {
72 | command
73 | .addArgument("--out")
74 | .addArgument(preparePath(tempPath));
75 | }
76 |
77 | command
78 | .addArgument("--config")
79 | .addArgument(preparePath(config.getConfigFile()));
80 |
81 | command.setNewShell(false);
82 |
83 | return command;
84 | }
85 |
86 | private String preparePath(String path) {
87 | if (path == null) {
88 | return "";
89 | } else if (path.contains(" ") && mustQuoteSpaceContainingPaths) {
90 | return '"' + path + '"';
91 | } else {
92 | return path;
93 | }
94 | }
95 |
96 | private String getCommandOutput(Command thisCommand, StreamConsumer stdOutConsumer, StreamConsumer stdErrConsumer, File rubocopOutputFile, Integer timeoutMs) {
97 | createExecutor().execute(thisCommand, stdOutConsumer, stdErrConsumer, timeoutMs);
98 | return getFileContent(rubocopOutputFile);
99 | }
100 |
101 | protected CommandExecutor createExecutor() {
102 | return CommandExecutor.create();
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/rubocop/RubocopExecutorConfig.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.rubocop;
2 |
3 | import static org.apache.commons.lang3.StringUtils.isNotBlank;
4 |
5 | import com.fortitudetec.sonar.plugins.ruby.PathResolver;
6 | import com.fortitudetec.sonar.plugins.ruby.RubyPlugin;
7 | import lombok.Getter;
8 | import lombok.Setter;
9 | import org.sonar.api.batch.sensor.SensorContext;
10 |
11 | @Getter
12 | @Setter
13 | public class RubocopExecutorConfig {
14 |
15 | private String pathToRubocopOutput;
16 | private String pathToRubocop;
17 | private String pathToRubocopFiles;
18 | private String configFile;
19 | private Integer timeoutMs;
20 |
21 | public static RubocopExecutorConfig fromSettings(SensorContext ctx, PathResolver resolver) {
22 | RubocopExecutorConfig toReturn = new RubocopExecutorConfig();
23 |
24 | toReturn.setPathToRubocop(resolver.getPath(ctx, RubyPlugin.RUBOCOP_BIN, "rubocop"));
25 | toReturn.setPathToRubocopFiles(resolver.getPath(ctx, RubyPlugin.RUBOCOP_FILES_PATH, "."));
26 | toReturn.setConfigFile(resolver.getPath(ctx, RubyPlugin.RUBOCOP_CONFIG, ""));
27 | toReturn.setPathToRubocopOutput(resolver.getPath(ctx, RubyPlugin.RUBOCOP_REPORT_PATH, "rubocop-result.json"));
28 |
29 | toReturn.setTimeoutMs(6000);
30 |
31 | return toReturn;
32 | }
33 |
34 | public Boolean useExistingRubocopOutput() {
35 | return isNotBlank(pathToRubocopOutput);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/rubocop/RubocopParser.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.rubocop;
2 |
3 | import static java.util.stream.Collectors.toMap;
4 |
5 | import com.fasterxml.jackson.databind.ObjectMapper;
6 | import com.fortitudetec.sonar.plugins.ruby.model.RubocopIssue;
7 | import com.fortitudetec.sonar.plugins.ruby.model.RubocopPosition;
8 | import com.fortitudetec.sonar.plugins.ruby.rubocop.model.RubocopFile;
9 | import com.fortitudetec.sonar.plugins.ruby.rubocop.model.RubocopOffense;
10 | import com.fortitudetec.sonar.plugins.ruby.rubocop.model.RubocopResult;
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 | import org.sonar.api.batch.BatchSide;
14 |
15 | import java.io.IOException;
16 | import java.util.Collections;
17 | import java.util.List;
18 | import java.util.Map;
19 | import java.util.stream.Collectors;
20 |
21 | @BatchSide
22 | public class RubocopParser {
23 | private static final Logger LOG = LoggerFactory.getLogger(RubocopParser.class);
24 | private static final ObjectMapper MAPPER = new ObjectMapper();
25 |
26 | public Map> parse(String toParse) {
27 | return collectIssues(toParse).entrySet().stream()
28 | .filter(entry -> !entry.getValue().isEmpty())
29 | .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
30 | }
31 |
32 | @SuppressWarnings("unchecked")
33 | private Map> collectIssues(String resultsFile) {
34 | try {
35 | RubocopResult results = MAPPER.readValue(resultsFile, RubocopResult.class);
36 |
37 | return results.getFiles().stream()
38 | .collect(toMap(RubocopFile::getPath, this::populateIssues));
39 | } catch (IOException e) {
40 | LOG.warn("Unable to parse results file", e);
41 | }
42 |
43 | return Collections.emptyMap();
44 | }
45 |
46 | @SuppressWarnings("unchecked")
47 | private List populateIssues(RubocopFile file) {
48 | return file.getOffenses().stream()
49 | .map(offense -> RubocopIssue.builder()
50 | .ruleName(offense.getCopName())
51 | .failure(offense.getMessage())
52 | .position(populatePosition(offense))
53 | .build())
54 | .collect(Collectors.toList());
55 | }
56 |
57 | private RubocopPosition populatePosition(RubocopOffense offense) {
58 | return RubocopPosition.builder()
59 | .line(offense.getLocation().get("line").intValue())
60 | .character(offense.getLocation().get("column").intValue())
61 | .build();
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/rubocop/RubocopSensor.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.rubocop;
2 |
3 | import static com.google.common.collect.Lists.newArrayList;
4 |
5 | import com.fortitudetec.sonar.plugins.ruby.PathResolver;
6 | import com.fortitudetec.sonar.plugins.ruby.Ruby;
7 | import com.fortitudetec.sonar.plugins.ruby.RubyRulesDefinition;
8 | import com.fortitudetec.sonar.plugins.ruby.model.RubocopIssue;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 | import org.sonar.api.batch.fs.FileSystem;
12 | import org.sonar.api.batch.fs.InputFile;
13 | import org.sonar.api.batch.rule.ActiveRule;
14 | import org.sonar.api.batch.sensor.Sensor;
15 | import org.sonar.api.batch.sensor.SensorContext;
16 | import org.sonar.api.batch.sensor.SensorDescriptor;
17 | import org.sonar.api.batch.sensor.issue.NewIssue;
18 | import org.sonar.api.batch.sensor.issue.NewIssueLocation;
19 | import org.sonar.api.rule.RuleKey;
20 |
21 | import java.io.File;
22 | import java.util.Collection;
23 | import java.util.List;
24 | import java.util.Map;
25 | import java.util.Set;
26 | import java.util.stream.Collectors;
27 | import java.util.stream.StreamSupport;
28 |
29 | public class RubocopSensor implements Sensor {
30 | private static final Logger LOG = LoggerFactory.getLogger(RubocopSensor.class);
31 |
32 | private PathResolver resolver;
33 | private RubocopExecutor executor;
34 | private RubocopParser parser;
35 |
36 | public RubocopSensor(PathResolver resolver, RubocopExecutor executor, RubocopParser parser) {
37 | this.resolver = resolver;
38 | this.executor = executor;
39 | this.parser = parser;
40 | }
41 |
42 | @Override
43 | public void describe(SensorDescriptor desc) {
44 | desc
45 | .name("Linting sensor for Ruby files")
46 | .onlyOnLanguage(Ruby.LANGUAGE_KEY);
47 | }
48 |
49 | @Override
50 | public void execute(SensorContext sensorContext) {
51 | RubocopExecutorConfig config = RubocopExecutorConfig.fromSettings(sensorContext, resolver);
52 |
53 | if (!config.useExistingRubocopOutput() && config.getPathToRubocop() == null) {
54 | LOG.warn("Path to rubocop not defined or not found. Skipping rubocop analysis.");
55 | return;
56 | }
57 |
58 | String jsonResults = this.executor.execute(config, findFilesToLint(sensorContext, config));
59 |
60 | Map> issues = parser.parse(jsonResults);
61 |
62 | if (issues.isEmpty()) {
63 | LOG.warn("Rubocop returned no result at all");
64 | return;
65 | }
66 |
67 | Collection allRules = sensorContext.activeRules().findByRepository("rubocop");
68 | Set ruleNames = allRules.stream().map(rule -> rule.ruleKey().rule()).collect(Collectors.toSet());
69 |
70 | issues.entrySet().forEach(rubyFilesIssues -> generateSonarIssuesFromResults(rubyFilesIssues, sensorContext, ruleNames));
71 | }
72 |
73 | private List findFilesToLint(SensorContext context, RubocopExecutorConfig config) {
74 | if (config.useExistingRubocopOutput()) {
75 | return newArrayList();
76 | }
77 |
78 | Iterable inputFiles = context.fileSystem().inputFiles(context.fileSystem().predicates().hasLanguage(Ruby.LANGUAGE_KEY));
79 |
80 | return StreamSupport.stream(inputFiles.spliterator(), false)
81 | .map(InputFile::absolutePath)
82 | .collect(Collectors.toList());
83 | }
84 |
85 | private void generateSonarIssuesFromResults(Map.Entry> rubyFilesIssues, SensorContext sensorContext, Set ruleNames) {
86 | List batchIssues = rubyFilesIssues.getValue();
87 |
88 | if (batchIssues.isEmpty()) {
89 | return;
90 | }
91 |
92 | String filePath = rubyFilesIssues.getKey();
93 | InputFile inputFile = findMatchingFile(sensorContext.fileSystem(), filePath);
94 | if (inputFile == null) {
95 | LOG.warn("Rubocop reported issues against a file that isn't in the analysis set - will be ignored: {}", filePath);
96 | return;
97 | }
98 |
99 | if (LOG.isDebugEnabled()) {
100 | LOG.debug("Handling Rubocop output for '{}' reporting against '{}'", filePath, inputFile.absolutePath());
101 | }
102 |
103 | batchIssues.forEach(issue -> saveNewIssue(issue, inputFile, ruleNames, sensorContext));
104 | }
105 |
106 | private InputFile findMatchingFile(FileSystem fs, String filePath) {
107 | File matchingFile = fs.resolvePath(filePath);
108 |
109 | if (matchingFile != null) {
110 | try {
111 | return fs.inputFile(fs.predicates().is(matchingFile));
112 | }
113 | catch (IllegalArgumentException e) {
114 | LOG.error("Failed to resolve " + filePath + " to a single path", e);
115 | }
116 | }
117 | return null;
118 | }
119 |
120 | private void saveNewIssue(RubocopIssue issue, InputFile inputFile, Set ruleNames, SensorContext sensorContext) {
121 | // Make sure the rule we're violating is one we recognise - if not, we'll
122 | // fall back to the generic 'rubocop-issue' rule
123 | String ruleName = issue.getRuleName();
124 | if (!ruleNames.contains(ruleName)) {
125 | ruleName = RubyRulesDefinition.RUBY_LINT_UNKNOWN_RULE.key;
126 | }
127 |
128 | NewIssue newIssue =
129 | sensorContext
130 | .newIssue()
131 | .forRule(RuleKey.of("rubocop", ruleName));
132 |
133 | NewIssueLocation newIssueLocation =
134 | newIssue
135 | .newLocation()
136 | .on(inputFile)
137 | .message(issue.getFailure())
138 | .at(inputFile.selectLine(issue.getPosition().getLine()));
139 |
140 | newIssue.at(newIssueLocation);
141 | newIssue.save();
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/rubocop/model/RubocopFile.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.rubocop.model;
2 |
3 | import lombok.Getter;
4 | import lombok.Setter;
5 |
6 | import java.util.List;
7 |
8 | @Getter
9 | @Setter
10 | public class RubocopFile {
11 | private String path;
12 | private List offenses;
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/rubocop/model/RubocopOffense.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.rubocop.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import lombok.Getter;
5 | import lombok.Setter;
6 |
7 | import java.util.Map;
8 |
9 | @Getter
10 | @Setter
11 | public class RubocopOffense {
12 | private String severity;
13 | private String message;
14 | private Boolean corrected;
15 | private Map location;
16 |
17 | @JsonProperty("cop_name")
18 | private String copName;
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/rubocop/model/RubocopResult.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.rubocop.model;
2 |
3 | import lombok.Getter;
4 | import lombok.Setter;
5 |
6 | import java.util.List;
7 | import java.util.Map;
8 |
9 | @Getter
10 | @Setter
11 | public class RubocopResult {
12 |
13 | private Map metadata;
14 | private List files;
15 | private Map summary;
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/simplecov/SimpleCovParser.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.simplecov;
2 |
3 | import static java.util.Objects.nonNull;
4 | import static java.util.Objects.isNull;
5 |
6 | import com.fasterxml.jackson.databind.ObjectMapper;
7 | import com.fortitudetec.sonar.plugins.ruby.Ruby;
8 | import com.fortitudetec.sonar.plugins.ruby.RubyPlugin;
9 | import org.apache.commons.io.FileUtils;
10 | import org.apache.commons.lang3.StringUtils;
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 | import org.sonar.api.batch.BatchSide;
14 | import org.sonar.api.batch.fs.InputFile;
15 | import org.sonar.api.batch.sensor.SensorContext;
16 | import org.sonar.api.batch.sensor.coverage.CoverageType;
17 | import org.sonar.api.batch.sensor.coverage.NewCoverage;
18 |
19 | import java.io.BufferedReader;
20 | import java.io.File;
21 | import java.io.FileNotFoundException;
22 | import java.io.FileReader;
23 | import java.io.IOException;
24 | import java.util.HashSet;
25 | import java.util.List;
26 | import java.util.Map;
27 | import java.util.Set;
28 | import java.util.stream.Collectors;
29 | import java.util.stream.IntStream;
30 | import java.util.stream.StreamSupport;
31 |
32 | @BatchSide
33 | public class SimpleCovParser {
34 | private static final Logger LOG = LoggerFactory.getLogger(SimpleCovParser.class);
35 |
36 | private static final ObjectMapper MAPPER = new ObjectMapper();
37 |
38 | @SuppressWarnings("unchecked")
39 | public Map parse(SensorContext ctx, File resultFile) throws IOException {
40 |
41 | Map>>> results = parseResultsFile(resultFile);
42 |
43 | String testFramework = ctx.settings().getString(RubyPlugin.TEST_FRAMEWORK);
44 |
45 | if (isNull(testFramework)) {
46 | LOG.warn("Test framework is not set, unable to parse coverage metrics");
47 | return null;
48 | }
49 |
50 | Map> coverageByFile = results.get(testFramework).get("coverage");
51 |
52 | Iterable inputFiles = ctx.fileSystem().inputFiles(ctx.fileSystem().predicates().hasLanguage(Ruby.LANGUAGE_KEY));
53 |
54 | return StreamSupport.stream(inputFiles.spliterator(), false)
55 | .collect(Collectors.toMap(InputFile::absolutePath,
56 | file -> buildCoverageForFile(ctx, file, coverageByFile.get(file.absolutePath()))));
57 | }
58 |
59 | private NewCoverage buildCoverageForFile(SensorContext ctx, InputFile file, List lineCounts) {
60 | NewCoverage coverage = ctx.newCoverage()
61 | .onFile(file)
62 | .ofType(CoverageType.UNIT);
63 |
64 | if (lineCounts == null || lineCounts.isEmpty()) {
65 | updateForZeroCoverage(file, coverage, gatherNonCommentLinesOfCodeForFile(file));
66 | } else {
67 | IntStream.range(0, lineCounts.size())
68 | .forEach(idx -> coverageForLine(coverage, idx, lineCounts.get(idx)));
69 | }
70 |
71 | return coverage;
72 | }
73 |
74 | private void coverageForLine(NewCoverage coverage, int lineNumber, Integer lineCount) {
75 | if (nonNull(lineCount)) {
76 | coverage.lineHits(lineNumber + 1, lineCount);
77 | }
78 | }
79 |
80 | private Map parseResultsFile(File resultFile) throws IOException {
81 | String fileString = FileUtils.readFileToString(resultFile, "UTF-8");
82 | return MAPPER.readValue(fileString, Map.class);
83 | }
84 |
85 | private Set gatherNonCommentLinesOfCodeForFile(InputFile inputFile) {
86 | HashSet toReturn = new HashSet<>();
87 |
88 | int lineNumber = 0;
89 |
90 | try (FileReader fileReader = new FileReader(inputFile.file()); BufferedReader reader = new BufferedReader(fileReader)) {
91 | String line;
92 | while (nonNull(line = reader.readLine())) {
93 | lineNumber++;
94 | line = line.trim().replaceAll("\\n|\\t|\\s", "");
95 | if (!(StringUtils.isBlank(line) || line.startsWith("#"))) {
96 | toReturn.add(lineNumber);
97 | }
98 | }
99 |
100 | reader.close();
101 |
102 | } catch (FileNotFoundException e) {
103 | LOG.error("File not found", e);
104 | } catch (IOException e) {
105 | LOG.error("Error while reading BufferedReader", e);
106 | }
107 |
108 | return toReturn;
109 | }
110 |
111 | private void updateForZeroCoverage(InputFile inputFile, NewCoverage newCoverage, Set nonCommentLineNumbers) {
112 | if (nonCommentLineNumbers != null) {
113 | nonCommentLineNumbers.forEach(lineNumber -> newCoverage.lineHits(lineNumber, 0));
114 | }
115 | else {
116 | IntStream.rangeClosed(0, inputFile.lines())
117 | .forEach(idx -> newCoverage.lineHits(idx, 0));
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/simplecov/SimpleCovSensor.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.simplecov;
2 |
3 | import static java.util.Objects.isNull;
4 |
5 | import com.fortitudetec.sonar.plugins.ruby.RubyPlugin;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.sonar.api.batch.BatchSide;
9 | import org.sonar.api.batch.sensor.SensorContext;
10 | import org.sonar.api.batch.sensor.coverage.NewCoverage;
11 |
12 | import java.io.File;
13 | import java.io.IOException;
14 | import java.util.Map;
15 |
16 | @BatchSide
17 | public class SimpleCovSensor {
18 | private static final Logger LOG = LoggerFactory.getLogger(SimpleCovSensor.class);
19 |
20 | public void execute(SensorContext context) {
21 | String reportPath = context.settings().getString(RubyPlugin.SIMPLECOV_REPORT_PATH);
22 |
23 | if (isNull(reportPath)) {
24 | LOG.warn("Report path is not set, unable to generate coverage metrics");
25 | return;
26 | }
27 |
28 | SimpleCovParser parser = new SimpleCovParser();
29 |
30 | try {
31 | Map fileCoverages = parser.parse(context, new File(reportPath));
32 | fileCoverages.values().forEach(NewCoverage::save);
33 | } catch (IOException e) {
34 | LOG.warn("Unable to generate coverage metrics", e);
35 | }
36 | }
37 |
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/squid/RubyFootPrint.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.squid;
2 |
3 | import com.fortitudetec.sonar.plugins.ruby.Ruby;
4 | import org.sonar.squidbridge.recognizer.Detector;
5 | import org.sonar.squidbridge.recognizer.EndWithDetector;
6 | import org.sonar.squidbridge.recognizer.KeywordsDetector;
7 | import org.sonar.squidbridge.recognizer.LanguageFootprint;
8 |
9 | import java.util.HashSet;
10 | import java.util.Set;
11 |
12 | public class RubyFootPrint implements LanguageFootprint {
13 | private static final double END_WITH_DETECTOR = 0.95;
14 | private static final double KEYWORDS_DETECTOR = 0.3;
15 |
16 | private final Set detectors = new HashSet<>();
17 |
18 | RubyFootPrint() {
19 | detectors.add(new EndWithDetector(END_WITH_DETECTOR, ')', '"', '\''));
20 | detectors.add(new KeywordsDetector(KEYWORDS_DETECTOR, Ruby.RUBY_KEYWORDS_ARRAY));
21 | }
22 |
23 | @Override
24 | public Set getDetectors() {
25 | return detectors;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/fortitudetec/sonar/plugins/ruby/squid/RubyRecognizer.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.squid;
2 |
3 | import org.sonar.squidbridge.recognizer.CodeRecognizer;
4 |
5 | public class RubyRecognizer extends CodeRecognizer {
6 | private static final double MAX_RECOGNIZE_PERCENT = 0.95;
7 |
8 | public RubyRecognizer() {
9 | super(MAX_RECOGNIZE_PERCENT, new RubyFootPrint());
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/test/java/com/fortitudetec/sonar/plugins/ruby/PathResolverTest.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 |
5 | import org.junit.Before;
6 | import org.junit.Test;
7 | import org.sonar.api.batch.fs.internal.DefaultInputFile;
8 | import org.sonar.api.batch.sensor.internal.SensorContextTester;
9 |
10 | import java.io.File;
11 | import java.net.URL;
12 |
13 | public class PathResolverTest {
14 | private PathResolver resolver;
15 | private SensorContextTester sensorContext;
16 |
17 | private File existingFile;
18 |
19 | @Before
20 | public void setUp() throws Exception {
21 | URL filePath = PathResolverTest.class.getClassLoader().getResource("./test_controller.rb");
22 | existingFile = new File(filePath.toURI());
23 | String parentPath = existingFile.getParent();
24 |
25 | this.sensorContext = SensorContextTester.create(new File(parentPath));
26 | this.sensorContext.settings().setProperty("path key", "test_controller.rb");
27 |
28 | DefaultInputFile file =
29 | new DefaultInputFile("", "test_controller.rb")
30 | .setLanguage(Ruby.LANGUAGE_KEY);
31 |
32 | this.sensorContext.fileSystem().add(file);
33 |
34 | this.resolver = new PathResolver();
35 | }
36 |
37 | @Test
38 | public void returnsAbsolutePathToFile_ifSpecifiedAndExists() {
39 | String result = this.resolver.getPath(this.sensorContext, "path key", "not me");
40 | assertSamePath(this.existingFile, result);
41 | }
42 |
43 | @Test
44 | public void returnsAbsolutePathToFallbackFile_ifPrimaryNotConfiguredAndFallbackExists() {
45 | String result = this.resolver.getPath(this.sensorContext, "new path key", "test_controller.rb");
46 | assertSamePath(this.existingFile, result);
47 | }
48 |
49 | @Test
50 | public void returnsAbsolutePathToFallbackFile_ifPrimaryNotConfiguredButEmptyAndFallbackExists() {
51 | this.sensorContext.settings().setProperty("new path key", "");
52 | String result = this.resolver.getPath(this.sensorContext, "new path key", "test_controller.rb");
53 | assertSamePath(this.existingFile, result);
54 | }
55 |
56 | @Test
57 | public void returnsNull_ifPrimaryNotConfiguredAndFallbackNull() {
58 | String result = this.resolver.getPath(this.sensorContext, "new path key", null);
59 | assertThat(result).isNull();
60 | }
61 |
62 | @Test
63 | public void returnsNull_ifRequestedPathDoesNotExist() {
64 | this.sensorContext.settings().setProperty("new path key", "missing.ts");
65 | String result = this.resolver.getPath(this.sensorContext, "new path key", "test_controller.rb");
66 | assertThat(result).isNull();
67 | }
68 |
69 | @Test
70 | public void returnsAbsolutePathToFile_ifAlreadyAbsoluteAndExists() {
71 | this.sensorContext.settings().setProperty("new path key", this.existingFile.getAbsolutePath());
72 | String result = this.resolver.getPath(this.sensorContext, "new path key", "not me");
73 | assertSamePath(this.existingFile, result);
74 | }
75 |
76 | private void assertSamePath(File existingFile, String argument) {
77 | if (argument == null) {
78 | assertThat(existingFile).isNull();
79 | } else {
80 | assertThat(existingFile).isEqualTo(new File(argument));
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/test/java/com/fortitudetec/sonar/plugins/ruby/RubyPluginTest.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 |
5 | import org.junit.Test;
6 | import org.sonar.api.Plugin;
7 | import org.sonar.api.config.PropertyDefinition;
8 | import org.sonar.api.utils.Version;
9 |
10 | public class RubyPluginTest {
11 |
12 | @SuppressWarnings("unchecked")
13 | @Test
14 | public void testDefine() {
15 | RubyPlugin plugin = new RubyPlugin();
16 |
17 | Plugin.Context context = new Plugin.Context(Version.create(1, 0));
18 |
19 | plugin.define(context);
20 |
21 | assertThat(context.getExtensions().size()).isEqualTo(18);
22 |
23 | assertThat(context.getExtensions().stream().filter(ext -> ext instanceof PropertyDefinition).count()).isEqualTo(7);
24 | assertThat(context.getExtensions().stream().filter(ext -> ext instanceof Class).count()).isEqualTo(11);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/test/java/com/fortitudetec/sonar/plugins/ruby/RubyRuleProfileTest.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 |
5 | import org.junit.Test;
6 | import org.sonar.api.profiles.RulesProfile;
7 |
8 | public class RubyRuleProfileTest {
9 |
10 | @Test
11 | public void testCreateProfile() {
12 | RubyRuleProfile rubyProfile = new RubyRuleProfile();
13 |
14 | RulesProfile profile = rubyProfile.createProfile(null);
15 |
16 | assertThat(profile.getName()).isEqualTo("Rubocop");
17 | assertThat(profile.getLanguage()).isEqualTo("rb");
18 | assertThat(profile.getActiveRules().size()).isEqualTo(341);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/test/java/com/fortitudetec/sonar/plugins/ruby/RubyRulesDefinitionTest.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby;
2 |
3 |
4 | import static org.assertj.core.api.Assertions.assertThat;
5 |
6 | import com.fortitudetec.sonar.plugins.ruby.model.RubocopRule;
7 | import org.junit.Before;
8 | import org.junit.Test;
9 | import org.sonar.api.rule.Severity;
10 | import org.sonar.api.rules.RuleType;
11 | import org.sonar.api.server.rule.RulesDefinition;
12 |
13 | import java.util.List;
14 | import java.util.stream.Collectors;
15 |
16 | public class RubyRulesDefinitionTest {
17 |
18 | private RubyRulesDefinition rules;
19 |
20 | @Before
21 | public void setUp() {
22 | rules = new RubyRulesDefinition();
23 | }
24 |
25 | @Test
26 | public void testGetCoreRules() {
27 | List coreRules = rules.getCoreRules();
28 |
29 | assertThat(coreRules.size()).isEqualTo(340);
30 |
31 | assertSeverityCount(Severity.BLOCKER, coreRules, 0);
32 | assertSeverityCount(Severity.CRITICAL, coreRules, 4);
33 | assertSeverityCount(Severity.MAJOR, coreRules, 0);
34 | assertSeverityCount(Severity.MINOR, coreRules, 324);
35 | assertSeverityCount(Severity.INFO, coreRules, 12);
36 |
37 | assertTypeCount(RuleType.CODE_SMELL, coreRules, 311);
38 | assertTypeCount(RuleType.VULNERABILITY, coreRules, 4);
39 | assertTypeCount(RuleType.BUG, coreRules, 25);
40 | }
41 |
42 | @Test
43 | public void testDefine() {
44 | RulesDefinition.Context context = new RulesDefinition.Context();
45 |
46 | rules.define(context);
47 |
48 | assertThat(context.repositories().size()).isEqualTo(1);
49 | assertThat(context.repository("rubocop")).isNotNull();
50 | assertThat(context.repository("rubocop").language()).isEqualTo("rb");
51 | assertThat(context.repository("rubocop").rules().size()).isEqualTo(341);
52 | }
53 |
54 |
55 | private void assertSeverityCount(String severity, List rules, int expectedCount) {
56 | List filteredRules = rules.stream()
57 | .filter(rule -> severity.equals(rule.severity))
58 | .collect(Collectors.toList());
59 |
60 | assertThat(filteredRules.size()).isEqualTo(expectedCount);
61 | }
62 |
63 | private void assertTypeCount(RuleType type, List rules, int expectedCount) {
64 | List filteredRules = rules.stream()
65 | .filter(rule -> type.equals(rule.debtType))
66 | .collect(Collectors.toList());
67 |
68 | assertThat(filteredRules.size()).isEqualTo(expectedCount);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/test/java/com/fortitudetec/sonar/plugins/ruby/RubyTest.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 |
5 | import com.google.common.collect.Lists;
6 | import org.junit.Test;
7 | import org.sonar.api.config.Settings;
8 |
9 | public class RubyTest {
10 |
11 | @Test
12 | public void testGetFileSuffixes_Default() {
13 | Ruby ruby = new Ruby(new Settings());
14 |
15 | String[] fileSuffixes = ruby.getFileSuffixes();
16 |
17 | assertThat(fileSuffixes).hasSize(8);
18 | assertThat(fileSuffixes).hasSameElementsAs(Lists.newArrayList("rb", "Gemfile", "gemspec", "rake", "spec", "Capfile", "ru", "Rakefile"));
19 | }
20 |
21 | @Test
22 | public void testGetFileSuffixes_Override() {
23 | Settings settings = new Settings();
24 | settings.setProperty("sonar.ruby.file.suffixes", "rb");
25 |
26 | Ruby ruby = new Ruby(settings);
27 |
28 | String[] fileSuffixes = ruby.getFileSuffixes();
29 |
30 | assertThat(fileSuffixes).hasSize(1);
31 | assertThat(fileSuffixes).hasSameElementsAs(Lists.newArrayList("rb"));
32 | }
33 |
34 | @Test
35 | public void testGetFileSuffixes_WithSpaces() {
36 | Settings settings = new Settings();
37 | settings.setProperty("sonar.ruby.file.suffixes", "rb, , gemspec");
38 |
39 | Ruby ruby = new Ruby(settings);
40 |
41 | String[] fileSuffixes = ruby.getFileSuffixes();
42 |
43 | assertThat(fileSuffixes).hasSize(2);
44 | assertThat(fileSuffixes).hasSameElementsAs(Lists.newArrayList("rb", "gemspec"));
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/test/java/com/fortitudetec/sonar/plugins/ruby/metrics/ClassCountParserTest.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.metrics;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 |
5 | import org.junit.Test;
6 |
7 | import java.io.File;
8 | import java.net.URISyntaxException;
9 | import java.net.URL;
10 |
11 | public class ClassCountParserTest {
12 |
13 | @Test
14 | public void testCountClasses() throws URISyntaxException {
15 | URL resource = ClassCountParser.class.getClassLoader().getResource("test_controller.rb");
16 | int linesOfComment = ClassCountParser.countClasses(new File(resource.toURI()));
17 | assertThat(linesOfComment).isEqualTo(1);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/test/java/com/fortitudetec/sonar/plugins/ruby/metrics/CombinedCoverageSensorTest.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.metrics;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 | import static org.mockito.Mockito.verify;
5 |
6 | import com.fortitudetec.sonar.plugins.ruby.Ruby;
7 | import com.fortitudetec.sonar.plugins.ruby.simplecov.SimpleCovSensor;
8 | import org.junit.Before;
9 | import org.junit.Test;
10 | import org.mockito.Mock;
11 | import org.mockito.MockitoAnnotations;
12 | import org.sonar.api.batch.sensor.SensorContext;
13 | import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
14 | import org.sonar.api.batch.sensor.internal.SensorContextTester;
15 |
16 | import java.io.File;
17 |
18 | public class CombinedCoverageSensorTest {
19 |
20 | @Mock
21 | private RubyMetricsSensor locSensor;
22 |
23 | @Mock
24 | private SimpleCovSensor coverageSensor;
25 |
26 | @Before
27 | public void setup() {
28 | MockitoAnnotations.initMocks(this);
29 | }
30 |
31 | @Test
32 | public void testExecute() {
33 | CombinedCoverageSensor sensor = new CombinedCoverageSensor(locSensor, coverageSensor);
34 |
35 | SensorContextTester context = SensorContextTester.create(new File("."));
36 | sensor.execute(context);
37 |
38 | verify(locSensor).execute(context);
39 | verify(coverageSensor).execute(context);
40 | }
41 |
42 | @Test
43 | public void testDescribe() {
44 | CombinedCoverageSensor sensor = new CombinedCoverageSensor(locSensor, coverageSensor);
45 |
46 | DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor();
47 | sensor.describe(descriptor);
48 |
49 | assertThat(descriptor.name()).isEqualTo("Combined SimpleCov and LOC sensor");
50 | assertThat(descriptor.languages()).containsExactly(Ruby.LANGUAGE_KEY);
51 |
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/test/java/com/fortitudetec/sonar/plugins/ruby/metrics/CommentCountParserTest.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.metrics;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 |
5 | import org.junit.Test;
6 |
7 | import java.io.File;
8 | import java.net.URISyntaxException;
9 | import java.net.URL;
10 |
11 | public class CommentCountParserTest {
12 |
13 | @Test
14 | public void testCountLinesOfComments() throws URISyntaxException {
15 | URL resource = CommentCountParserTest.class.getClassLoader().getResource("test_controller.rb");
16 | int linesOfComment = CommentCountParser.countLinesOfComment(new File(resource.toURI()));
17 | assertThat(linesOfComment).isEqualTo(11);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/test/java/com/fortitudetec/sonar/plugins/ruby/metrics/RubyMetricsSensorTest.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.metrics;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 |
5 | import com.fortitudetec.sonar.plugins.ruby.rubocop.RubocopSensorTest;
6 | import org.junit.Before;
7 | import org.junit.Test;
8 | import org.sonar.api.batch.fs.internal.DefaultInputFile;
9 | import org.sonar.api.batch.sensor.internal.SensorContextTester;
10 | import org.sonar.api.batch.sensor.measure.Measure;
11 | import org.sonar.api.measures.CoreMetrics;
12 |
13 | import java.io.File;
14 | import java.net.URISyntaxException;
15 | import java.net.URL;
16 | import java.util.Collection;
17 |
18 | public class RubyMetricsSensorTest {
19 |
20 | private SensorContextTester context;
21 | private File rubyFile;
22 |
23 | @Before
24 | public void setUp() throws URISyntaxException {
25 | URL filePath = RubocopSensorTest.class.getClassLoader().getResource("./test_controller.rb");
26 | rubyFile = new File(filePath.toURI());
27 |
28 | context = SensorContextTester.create(rubyFile.getParentFile());
29 | context.fileSystem().add(new DefaultInputFile("myProjectKey", "test_controller.rb")
30 | .setLanguage("rb")
31 | .setOriginalLineOffsets(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15})
32 | .setLines(15));
33 | }
34 |
35 | @Test
36 | public void testExecute() {
37 | RubyMetricsSensor sensor = new RubyMetricsSensor();
38 |
39 | sensor.execute(context);
40 |
41 | Collection measures = context.measures("myProjectKey:test_controller.rb");
42 | assertThat(measures.size()).isEqualTo(3);
43 |
44 | measures.forEach(measure -> {
45 | if (CoreMetrics.NCLOC.equals(measure.metric())) {
46 | assertThat(measure.value()).isEqualTo(4);
47 | } else if (CoreMetrics.COMMENT_LINES.equals(measure.metric())) {
48 | assertThat(measure.value()).isEqualTo(11);
49 | } else if (CoreMetrics.CLASSES.equals(measure.metric())) {
50 | assertThat(measure.value()).isEqualTo(1);
51 | }
52 | });
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/test/java/com/fortitudetec/sonar/plugins/ruby/rubocop/RubocopParserTest.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.rubocop;
2 |
3 | import static com.google.common.collect.Lists.newArrayList;
4 | import static org.assertj.core.api.Assertions.assertThat;
5 |
6 | import com.fortitudetec.sonar.plugins.ruby.model.RubocopIssue;
7 | import com.google.common.base.Charsets;
8 | import com.google.common.io.Files;
9 | import org.junit.Before;
10 | import org.junit.Test;
11 | import org.sonar.api.batch.sensor.internal.SensorContextTester;
12 |
13 | import java.io.File;
14 | import java.io.IOException;
15 | import java.net.URISyntaxException;
16 | import java.net.URL;
17 | import java.nio.file.Paths;
18 | import java.util.List;
19 | import java.util.Map;
20 |
21 | public class RubocopParserTest {
22 |
23 | private SensorContextTester context;
24 |
25 | @Before
26 | public void setUp() throws URISyntaxException {
27 | context = SensorContextTester.create(Paths.get("."));
28 | }
29 |
30 | @Test
31 | public void testParseResultsFile() throws URISyntaxException, IOException {
32 | RubocopParser parser = new RubocopParser();
33 |
34 | URL resource = RubocopParser.class.getClassLoader().getResource("result.json");
35 | Map> results = parser.parse(Files.toString(new File(resource.toURI()), Charsets.UTF_8));
36 |
37 | assertThat(results.size()).isEqualTo(14);
38 | assertThat(results.get("test/controllers/logging_controller_test.rb").size()).isEqualTo(2);
39 | assertThat(results.get("test/controllers/logging_controller_test.rb").get(0).getRuleName()).isEqualTo("Style/StringLiterals");
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/test/java/com/fortitudetec/sonar/plugins/ruby/rubocop/RubocopSensorTest.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.rubocop;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 | import static org.mockito.Matchers.any;
5 | import static org.mockito.Matchers.isA;
6 | import static org.mockito.Mockito.when;
7 |
8 | import com.fortitudetec.sonar.plugins.ruby.PathResolver;
9 | import com.fortitudetec.sonar.plugins.ruby.Ruby;
10 | import com.fortitudetec.sonar.plugins.ruby.RubyPlugin;
11 | import com.fortitudetec.sonar.plugins.ruby.model.RubocopIssue;
12 | import com.fortitudetec.sonar.plugins.ruby.model.RubocopPosition;
13 | import com.google.common.collect.Lists;
14 | import org.junit.Before;
15 | import org.junit.Test;
16 | import org.mockito.Mock;
17 | import org.mockito.MockitoAnnotations;
18 | import org.sonar.api.batch.fs.internal.DefaultInputFile;
19 | import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
20 | import org.sonar.api.batch.sensor.internal.SensorContextTester;
21 | import org.sonar.api.utils.System2;
22 | import org.sonar.api.utils.internal.JUnitTempFolder;
23 |
24 | import java.io.File;
25 | import java.net.URISyntaxException;
26 | import java.net.URL;
27 | import java.util.HashMap;
28 | import java.util.List;
29 | import java.util.Map;
30 |
31 | public class RubocopSensorTest {
32 |
33 | private SensorContextTester context;
34 | private File rubyFile;
35 |
36 | @Mock
37 | private RubocopExecutor executor;
38 |
39 | @Mock
40 | private RubocopParser parser;
41 |
42 | @Before
43 | public void setUp() throws URISyntaxException {
44 | MockitoAnnotations.initMocks(this);
45 |
46 | URL filePath = RubocopSensorTest.class.getClassLoader().getResource("./test_controller.rb");
47 | rubyFile = new File(filePath.toURI());
48 |
49 | context = SensorContextTester.create(rubyFile.getParentFile());
50 | context.fileSystem().add(new DefaultInputFile("myProjectKey", "test_controller.rb")
51 | .setLanguage("rb")
52 | .setOriginalLineOffsets(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15})
53 | .setLines(15));
54 | context.fileSystem().add(new DefaultInputFile("myProjectKey", "unknown_file.rb")
55 | .setLanguage("rb")
56 | .setOriginalLineOffsets(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
57 | .setLines(10));
58 | }
59 |
60 | @Test
61 | public void testDescribe() {
62 | RubocopExecutor executor = new RubocopExecutor(System2.INSTANCE, new JUnitTempFolder());
63 |
64 | RubocopSensor sensor = new RubocopSensor(new PathResolver(), executor, new RubocopParser());
65 |
66 | DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor();
67 | sensor.describe(descriptor);
68 |
69 | assertThat(descriptor.name()).isEqualTo("Linting sensor for Ruby files");
70 | assertThat(descriptor.languages()).containsExactly(Ruby.LANGUAGE_KEY);
71 | }
72 |
73 | @Test
74 | public void testExecute_SkippingRubocop() {
75 | RubocopSensor sensor = new RubocopSensor(new PathResolver(), executor, parser);
76 | sensor.execute(context);
77 |
78 | assertThat(context.allIssues()).isEmpty();
79 | }
80 |
81 | @Test
82 | public void testExecute() throws URISyntaxException {
83 | String reportPaths = "rubocop-result.json";
84 | when(executor.execute(isA(RubocopExecutorConfig.class), any())).thenReturn(reportPaths);
85 | when(parser.parse(reportPaths)).thenReturn(buildIssues());
86 |
87 | URL filePath = RubocopSensorTest.class.getClassLoader().getResource("./result.json");
88 | File resultFile = new File(filePath.toURI());
89 | context.settings().setProperty(RubyPlugin.RUBOCOP_REPORT_PATH, resultFile.getAbsolutePath());
90 |
91 | RubocopSensor sensor = new RubocopSensor(new PathResolver(), executor, parser);
92 | sensor.execute(context);
93 |
94 | assertThat(context.allIssues().size()).isEqualTo(1);
95 | }
96 |
97 | private Map> buildIssues() {
98 | Map> issues = new HashMap<>();
99 |
100 | issues.put(rubyFile.getAbsolutePath(), Lists.newArrayList(
101 | RubocopIssue.builder()
102 | .failure("Foo")
103 | .position(RubocopPosition.builder()
104 | .line(1)
105 | .character(1)
106 | .build())
107 | .ruleName("Layout/AlignArray")
108 | .build()
109 | ));
110 |
111 | return issues;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/test/java/com/fortitudetec/sonar/plugins/ruby/simplecov/SimpleCovParserTest.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.simplecov;
2 |
3 | import com.fortitudetec.sonar.plugins.ruby.RubyPlugin;
4 | import com.fortitudetec.sonar.plugins.ruby.utils.TestUtils;
5 | import org.junit.After;
6 | import org.junit.Before;
7 | import org.junit.Test;
8 | import org.sonar.api.batch.fs.internal.DefaultInputFile;
9 | import org.sonar.api.batch.sensor.coverage.NewCoverage;
10 | import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
11 | import org.sonar.api.batch.sensor.internal.SensorContextTester;
12 |
13 | import java.io.File;
14 | import java.io.IOException;
15 | import java.net.URISyntaxException;
16 | import java.net.URL;
17 | import java.util.Map;
18 |
19 | import static org.assertj.core.api.Assertions.assertThat;
20 |
21 | public class SimpleCovParserTest {
22 |
23 | private SensorContextTester context;
24 | private File rubyFile;
25 |
26 | @Before
27 | public void setUp() throws URISyntaxException {
28 | URL filePath = SimpleCovParserTest.class.getClassLoader().getResource("./test_controller.rb");
29 | rubyFile = new File(filePath.toURI());
30 |
31 | context = SensorContextTester.create(rubyFile.getParentFile());
32 | context.settings().setProperty(RubyPlugin.TEST_FRAMEWORK, "RSpec");
33 | context.fileSystem().add(new DefaultInputFile("myProjectKey", "test_controller.rb")
34 | .setLanguage("rb")
35 | .setLines(15));
36 |
37 | TestUtils.buildResultSetFile(rubyFile);
38 | }
39 |
40 | @After
41 | public void tearDown() throws URISyntaxException {
42 | URL filePath = SimpleCovParserTest.class.getClassLoader().getResource(".resultset.json");
43 | File resultFile = new File(filePath.toURI());
44 | resultFile.delete();
45 | }
46 |
47 | @Test
48 | public void testParse() throws URISyntaxException, IOException {
49 | SimpleCovParser parser = new SimpleCovParser();
50 |
51 | URL filePath = SimpleCovParserTest.class.getClassLoader().getResource(".resultset.json");
52 | File resultFile = new File(filePath.toURI());
53 |
54 | Map coverage = parser.parse(context, resultFile);
55 |
56 | assertThat(coverage).isNotNull();
57 |
58 | DefaultCoverage defaultCoverage = (DefaultCoverage) coverage.get(rubyFile.getAbsolutePath());
59 | assertThat(defaultCoverage.coveredLines()).isEqualTo(4);
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/com/fortitudetec/sonar/plugins/ruby/simplecov/SimpleCovSensorTest.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.simplecov;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.fortitudetec.sonar.plugins.ruby.RubyPlugin;
5 | import com.fortitudetec.sonar.plugins.ruby.utils.TestUtils;
6 | import org.junit.After;
7 | import org.junit.Before;
8 | import org.junit.Test;
9 | import org.sonar.api.batch.fs.internal.DefaultInputFile;
10 | import org.sonar.api.batch.sensor.coverage.CoverageType;
11 | import org.sonar.api.batch.sensor.internal.SensorContextTester;
12 |
13 | import java.io.File;
14 | import java.net.URISyntaxException;
15 | import java.net.URL;
16 |
17 | import static org.assertj.core.api.Assertions.assertThat;
18 |
19 | public class SimpleCovSensorTest {
20 |
21 | private static final ObjectMapper MAPPER = new ObjectMapper();
22 |
23 | private SensorContextTester context;
24 | private File rubyFile;
25 |
26 | @Before
27 | public void setUp() throws URISyntaxException {
28 | URL filePath = SimpleCovSensorTest.class.getClassLoader().getResource("./test_controller.rb");
29 | rubyFile = new File(filePath.toURI());
30 |
31 | context = SensorContextTester.create(rubyFile.getParentFile());
32 | context.settings().setProperty(RubyPlugin.TEST_FRAMEWORK, "RSpec");
33 | context.fileSystem().add(new DefaultInputFile("myProjectKey", "test_controller.rb")
34 | .setLanguage("rb")
35 | .setLines(15));
36 | context.fileSystem().add(new DefaultInputFile("myProjectKey", "unknown_file.rb")
37 | .setLanguage("rb")
38 | .setLines(10));
39 |
40 | TestUtils.buildResultSetFile(rubyFile);
41 | }
42 |
43 | @After
44 | public void tearDown() throws URISyntaxException {
45 | URL filePath = SimpleCovSensorTest.class.getClassLoader().getResource(".resultset.json");
46 | File resultFile = new File(filePath.toURI());
47 | resultFile.delete();
48 | }
49 |
50 | @Test
51 | public void testExecute_EmptyReportPath() {
52 | SimpleCovSensor sensor = new SimpleCovSensor();
53 | sensor.execute(context);
54 |
55 | assertThat(context.lineHits(rubyFile.getAbsolutePath(), CoverageType.UNIT, 1)).isNull();
56 | }
57 |
58 | @Test
59 | public void testExecute() throws URISyntaxException {
60 | URL filePath = SimpleCovSensorTest.class.getClassLoader().getResource(".resultset.json");
61 | File resultFile = new File(filePath.toURI());
62 | context.settings().setProperty(RubyPlugin.SIMPLECOV_REPORT_PATH, resultFile.getAbsolutePath());
63 |
64 | SimpleCovSensor sensor = new SimpleCovSensor();
65 | sensor.execute(context);
66 |
67 | assertThat(context.lineHits("myProjectKey:test_controller.rb", CoverageType.UNIT, 1)).isEqualTo(1);
68 | }
69 |
70 | @Test
71 | public void testExecute_ZeroOutUnknownFiles() throws URISyntaxException {
72 | URL filePath = SimpleCovSensorTest.class.getClassLoader().getResource(".resultset.json");
73 | File resultFile = new File(filePath.toURI());
74 | context.settings().setProperty(RubyPlugin.SIMPLECOV_REPORT_PATH, resultFile.getAbsolutePath());
75 |
76 | SimpleCovSensor sensor = new SimpleCovSensor();
77 | sensor.execute(context);
78 |
79 | assertThat(context.lineHits("myProjectKey:unknown_file.rb", CoverageType.UNIT, 1)).isEqualTo(0);
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/src/test/java/com/fortitudetec/sonar/plugins/ruby/utils/TestUtils.java:
--------------------------------------------------------------------------------
1 | package com.fortitudetec.sonar.plugins.ruby.utils;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.google.common.collect.Lists;
5 | import com.google.common.collect.Maps;
6 | import org.apache.commons.io.IOUtils;
7 |
8 | import java.io.File;
9 | import java.io.FileOutputStream;
10 | import java.util.HashMap;
11 | import java.util.List;
12 | import java.util.Map;
13 |
14 | public class TestUtils {
15 |
16 | private static final ObjectMapper MAPPER = new ObjectMapper();
17 |
18 | public static void buildResultSetFile(File rubyFile) {
19 | Map results = new HashMap<>();
20 |
21 | Map coverage = Maps.newHashMap();
22 | List lines = Lists.newArrayList(1, null, null, 1, null, 1, null, null, null, null, null, null, null, null, 1);
23 | coverage.put(rubyFile.getAbsolutePath(), lines);
24 |
25 | Map testType = Maps.newHashMap();
26 | testType.put("coverage", coverage);
27 | testType.put("timestamp", 1505253204);
28 |
29 | results.put("RSpec", testType);
30 |
31 | try (FileOutputStream fileOut = new FileOutputStream(rubyFile.getParent() + File.separatorChar + ".resultset.json")) {
32 | IOUtils.write(MAPPER.writeValueAsString(results), fileOut);
33 | } catch (Exception e) {
34 | e.printStackTrace();
35 | }
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/resources/result.json:
--------------------------------------------------------------------------------
1 | {
2 | "metadata": {
3 | "rubocop_version": "0.37.0",
4 | "ruby_engine": "ruby",
5 | "ruby_version": "2.3.0",
6 | "ruby_patchlevel": "0",
7 | "ruby_platform": "x86_64-darwin15"
8 | },
9 | "files": [
10 | {
11 | "path": "config.ru",
12 | "offenses": []
13 | },
14 | {
15 | "path": "Gemfile",
16 | "offenses": [
17 | {
18 | "severity": "convention",
19 | "message": "Extra blank line detected.",
20 | "cop_name": "Style/EmptyLines",
21 | "corrected": false,
22 | "location": {
23 | "line": 3,
24 | "column": 1,
25 | "length": 1
26 | }
27 | },
28 | {
29 | "severity": "convention",
30 | "message": "Missing space after #.",
31 | "cop_name": "Style/LeadingCommentSpace",
32 | "corrected": false,
33 | "location": {
34 | "line": 16,
35 | "column": 1,
36 | "length": 37
37 | }
38 | },
39 | {
40 | "severity": "convention",
41 | "message": "Missing space after #.",
42 | "cop_name": "Style/LeadingCommentSpace",
43 | "corrected": false,
44 | "location": {
45 | "line": 18,
46 | "column": 1,
47 | "length": 46
48 | }
49 | },
50 | {
51 | "severity": "convention",
52 | "message": "Missing space after #.",
53 | "cop_name": "Style/LeadingCommentSpace",
54 | "corrected": false,
55 | "location": {
56 | "line": 19,
57 | "column": 1,
58 | "length": 30
59 | }
60 | }
61 | ]
62 | },
63 | {
64 | "path": "Rakefile",
65 | "offenses": []
66 | },
67 | {
68 | "path": "app/channels/application_cable/channel.rb",
69 | "offenses": [
70 | {
71 | "severity": "convention",
72 | "message": "Line is too long. [125/120]",
73 | "cop_name": "Metrics/LineLength",
74 | "corrected": null,
75 | "location": {
76 | "line": 1,
77 | "column": 121,
78 | "length": 5
79 | }
80 | }
81 | ]
82 | },
83 | {
84 | "path": "app/channels/application_cable/connection.rb",
85 | "offenses": [
86 | {
87 | "severity": "convention",
88 | "message": "Line is too long. [125/120]",
89 | "cop_name": "Metrics/LineLength",
90 | "corrected": null,
91 | "location": {
92 | "line": 1,
93 | "column": 121,
94 | "length": 5
95 | }
96 | }
97 | ]
98 | },
99 | {
100 | "path": "app/controllers/application_controller.rb",
101 | "offenses": []
102 | },
103 | {
104 | "path": "app/controllers/dashboard_controller.rb",
105 | "offenses": [
106 | {
107 | "severity": "convention",
108 | "message": "Extra empty line detected at method body beginning.",
109 | "cop_name": "Style/EmptyLinesAroundMethodBody",
110 | "corrected": false,
111 | "location": {
112 | "line": 3,
113 | "column": 1,
114 | "length": 1
115 | }
116 | }
117 | ]
118 | },
119 | {
120 | "path": "app/controllers/logging_controller.rb",
121 | "offenses": [
122 | {
123 | "severity": "convention",
124 | "message": "Prefer single-quoted strings when you don't need string interpolation or special symbols.",
125 | "cop_name": "Style/StringLiterals",
126 | "corrected": false,
127 | "location": {
128 | "line": 6,
129 | "column": 25,
130 | "length": 12
131 | }
132 | },
133 | {
134 | "severity": "convention",
135 | "message": "Prefer single-quoted strings when you don't need string interpolation or special symbols.",
136 | "cop_name": "Style/StringLiterals",
137 | "corrected": false,
138 | "location": {
139 | "line": 9,
140 | "column": 35,
141 | "length": 6
142 | }
143 | },
144 | {
145 | "severity": "convention",
146 | "message": "Space found before comma.",
147 | "cop_name": "Style/SpaceBeforeComma",
148 | "corrected": false,
149 | "location": {
150 | "line": 10,
151 | "column": 61,
152 | "length": 1
153 | }
154 | }
155 | ]
156 | },
157 | {
158 | "path": "app/helpers/application_helper.rb",
159 | "offenses": []
160 | },
161 | {
162 | "path": "app/helpers/logging_helper.rb",
163 | "offenses": []
164 | },
165 | {
166 | "path": "app/jobs/application_job.rb",
167 | "offenses": []
168 | },
169 | {
170 | "path": "app/mailers/application_mailer.rb",
171 | "offenses": []
172 | },
173 | {
174 | "path": "app/middleware/logging_backend.rb",
175 | "offenses": [
176 | {
177 | "severity": "convention",
178 | "message": "Surrounding space missing for operator `=`.",
179 | "cop_name": "Style/SpaceAroundOperators",
180 | "corrected": false,
181 | "location": {
182 | "line": 4,
183 | "column": 26,
184 | "length": 1
185 | }
186 | },
187 | {
188 | "severity": "convention",
189 | "message": "Use empty lines between method definitions.",
190 | "cop_name": "Style/EmptyLineBetweenDefs",
191 | "corrected": false,
192 | "location": {
193 | "line": 16,
194 | "column": 5,
195 | "length": 3
196 | }
197 | },
198 | {
199 | "severity": "convention",
200 | "message": "Missing space after #.",
201 | "cop_name": "Style/LeadingCommentSpace",
202 | "corrected": false,
203 | "location": {
204 | "line": 27,
205 | "column": 25,
206 | "length": 8
207 | }
208 | },
209 | {
210 | "severity": "convention",
211 | "message": "Method has too many lines. [29/20]",
212 | "cop_name": "Metrics/MethodLength",
213 | "corrected": null,
214 | "location": {
215 | "line": 29,
216 | "column": 5,
217 | "length": 3
218 | }
219 | },
220 | {
221 | "severity": "convention",
222 | "message": "Missing space after #.",
223 | "cop_name": "Style/LeadingCommentSpace",
224 | "corrected": false,
225 | "location": {
226 | "line": 30,
227 | "column": 7,
228 | "length": 68
229 | }
230 | },
231 | {
232 | "severity": "convention",
233 | "message": "Missing space after #.",
234 | "cop_name": "Style/LeadingCommentSpace",
235 | "corrected": false,
236 | "location": {
237 | "line": 31,
238 | "column": 7,
239 | "length": 53
240 | }
241 | },
242 | {
243 | "severity": "convention",
244 | "message": "Inconsistent indentation detected.",
245 | "cop_name": "Style/IndentationConsistency",
246 | "corrected": false,
247 | "location": {
248 | "line": 34,
249 | "column": 13,
250 | "length": 1306
251 | }
252 | },
253 | {
254 | "severity": "convention",
255 | "message": "Use the new Ruby 1.9 hash syntax.",
256 | "cop_name": "Style/HashSyntax",
257 | "corrected": false,
258 | "location": {
259 | "line": 35,
260 | "column": 31,
261 | "length": 8
262 | }
263 | },
264 | {
265 | "severity": "convention",
266 | "message": "Use the new Ruby 1.9 hash syntax.",
267 | "cop_name": "Style/HashSyntax",
268 | "corrected": false,
269 | "location": {
270 | "line": 35,
271 | "column": 76,
272 | "length": 8
273 | }
274 | },
275 | {
276 | "severity": "convention",
277 | "message": "Use the new Ruby 1.9 hash syntax.",
278 | "cop_name": "Style/HashSyntax",
279 | "corrected": false,
280 | "location": {
281 | "line": 35,
282 | "column": 91,
283 | "length": 11
284 | }
285 | },
286 | {
287 | "severity": "convention",
288 | "message": "Do not write to stdout. Use Rails' logger if you want to log.",
289 | "cop_name": "Rails/Output",
290 | "corrected": null,
291 | "location": {
292 | "line": 37,
293 | "column": 11,
294 | "length": 1
295 | }
296 | },
297 | {
298 | "severity": "warning",
299 | "message": "Unused block argument - `pchannel`. If it's necessary, use `_` or `_pchannel` as an argument name to indicate that it won't be used.",
300 | "cop_name": "Lint/UnusedBlockArgument",
301 | "corrected": false,
302 | "location": {
303 | "line": 38,
304 | "column": 27,
305 | "length": 8
306 | }
307 | },
308 | {
309 | "severity": "convention",
310 | "message": "Do not write to stdout. Use Rails' logger if you want to log.",
311 | "cop_name": "Rails/Output",
312 | "corrected": null,
313 | "location": {
314 | "line": 40,
315 | "column": 13,
316 | "length": 1
317 | }
318 | },
319 | {
320 | "severity": "convention",
321 | "message": "Missing space after #.",
322 | "cop_name": "Style/LeadingCommentSpace",
323 | "corrected": false,
324 | "location": {
325 | "line": 42,
326 | "column": 15,
327 | "length": 66
328 | }
329 | },
330 | {
331 | "severity": "convention",
332 | "message": "Use 2 (not -12) spaces for indentation.",
333 | "cop_name": "Style/IndentationWidth",
334 | "corrected": false,
335 | "location": {
336 | "line": 43,
337 | "column": 15,
338 | "length": 12
339 | }
340 | },
341 | {
342 | "severity": "convention",
343 | "message": "Do not write to stdout. Use Rails' logger if you want to log.",
344 | "cop_name": "Rails/Output",
345 | "corrected": null,
346 | "location": {
347 | "line": 44,
348 | "column": 17,
349 | "length": 1
350 | }
351 | },
352 | {
353 | "severity": "convention",
354 | "message": "Use the new Ruby 1.9 hash syntax.",
355 | "cop_name": "Style/HashSyntax",
356 | "corrected": false,
357 | "location": {
358 | "line": 47,
359 | "column": 19,
360 | "length": 8
361 | }
362 | },
363 | {
364 | "severity": "convention",
365 | "message": "Use the new Ruby 1.9 hash syntax.",
366 | "cop_name": "Style/HashSyntax",
367 | "corrected": false,
368 | "location": {
369 | "line": 48,
370 | "column": 19,
371 | "length": 11
372 | }
373 | },
374 | {
375 | "severity": "convention",
376 | "message": "Use the new Ruby 1.9 hash syntax.",
377 | "cop_name": "Style/HashSyntax",
378 | "corrected": false,
379 | "location": {
380 | "line": 49,
381 | "column": 19,
382 | "length": 8
383 | }
384 | },
385 | {
386 | "severity": "convention",
387 | "message": "Missing space after #.",
388 | "cop_name": "Style/LeadingCommentSpace",
389 | "corrected": false,
390 | "location": {
391 | "line": 52,
392 | "column": 15,
393 | "length": 66
394 | }
395 | },
396 | {
397 | "severity": "convention",
398 | "message": "Incorrect indentation detected (column 14 instead of 16).",
399 | "cop_name": "Style/CommentIndentation",
400 | "corrected": false,
401 | "location": {
402 | "line": 53,
403 | "column": 15,
404 | "length": 35
405 | }
406 | },
407 | {
408 | "severity": "convention",
409 | "message": "Missing space after #.",
410 | "cop_name": "Style/LeadingCommentSpace",
411 | "corrected": false,
412 | "location": {
413 | "line": 53,
414 | "column": 15,
415 | "length": 35
416 | }
417 | },
418 | {
419 | "severity": "convention",
420 | "message": "Do not write to stdout. Use Rails' logger if you want to log.",
421 | "cop_name": "Rails/Output",
422 | "corrected": null,
423 | "location": {
424 | "line": 54,
425 | "column": 17,
426 | "length": 1
427 | }
428 | },
429 | {
430 | "severity": "convention",
431 | "message": "Inconsistent indentation detected.",
432 | "cop_name": "Style/IndentationConsistency",
433 | "corrected": false,
434 | "location": {
435 | "line": 54,
436 | "column": 17,
437 | "length": 42
438 | }
439 | },
440 | {
441 | "severity": "convention",
442 | "message": "Inconsistent indentation detected.",
443 | "cop_name": "Style/IndentationConsistency",
444 | "corrected": false,
445 | "location": {
446 | "line": 55,
447 | "column": 17,
448 | "length": 129
449 | }
450 | },
451 | {
452 | "severity": "convention",
453 | "message": "Use the new Ruby 1.9 hash syntax.",
454 | "cop_name": "Style/HashSyntax",
455 | "corrected": false,
456 | "location": {
457 | "line": 56,
458 | "column": 19,
459 | "length": 8
460 | }
461 | },
462 | {
463 | "severity": "convention",
464 | "message": "Use the new Ruby 1.9 hash syntax.",
465 | "cop_name": "Style/HashSyntax",
466 | "corrected": false,
467 | "location": {
468 | "line": 57,
469 | "column": 19,
470 | "length": 7
471 | }
472 | },
473 | {
474 | "severity": "convention",
475 | "message": "Incorrect indentation detected (column 14 instead of 28).",
476 | "cop_name": "Style/CommentIndentation",
477 | "corrected": false,
478 | "location": {
479 | "line": 59,
480 | "column": 15,
481 | "length": 4
482 | }
483 | },
484 | {
485 | "severity": "convention",
486 | "message": "Missing space after #.",
487 | "cop_name": "Style/LeadingCommentSpace",
488 | "corrected": false,
489 | "location": {
490 | "line": 59,
491 | "column": 15,
492 | "length": 4
493 | }
494 | },
495 | {
496 | "severity": "warning",
497 | "message": "`end` at 60, 26 is not aligned with `@clients.each do |log_socket|` at 41, 12.",
498 | "cop_name": "Lint/BlockAlignment",
499 | "corrected": false,
500 | "location": {
501 | "line": 60,
502 | "column": 27,
503 | "length": 3
504 | }
505 | },
506 | {
507 | "severity": "convention",
508 | "message": "Do not write to stdout. Use Rails' logger if you want to log.",
509 | "cop_name": "Rails/Output",
510 | "corrected": null,
511 | "location": {
512 | "line": 61,
513 | "column": 13,
514 | "length": 1
515 | }
516 | },
517 | {
518 | "severity": "warning",
519 | "message": "`end` at 64, 6 is not aligned with `Thread.new do` at 34, 12.",
520 | "cop_name": "Lint/BlockAlignment",
521 | "corrected": false,
522 | "location": {
523 | "line": 64,
524 | "column": 7,
525 | "length": 3
526 | }
527 | },
528 | {
529 | "severity": "convention",
530 | "message": "Missing space after #.",
531 | "cop_name": "Style/LeadingCommentSpace",
532 | "corrected": false,
533 | "location": {
534 | "line": 68,
535 | "column": 7,
536 | "length": 73
537 | }
538 | },
539 | {
540 | "severity": "convention",
541 | "message": "Missing space after #.",
542 | "cop_name": "Style/LeadingCommentSpace",
543 | "corrected": false,
544 | "location": {
545 | "line": 69,
546 | "column": 7,
547 | "length": 70
548 | }
549 | },
550 | {
551 | "severity": "convention",
552 | "message": "Missing space after #.",
553 | "cop_name": "Style/LeadingCommentSpace",
554 | "corrected": false,
555 | "location": {
556 | "line": 70,
557 | "column": 7,
558 | "length": 23
559 | }
560 | },
561 | {
562 | "severity": "convention",
563 | "message": "Use a guard clause instead of wrapping the code inside a conditional expression.",
564 | "cop_name": "Style/GuardClause",
565 | "corrected": null,
566 | "location": {
567 | "line": 71,
568 | "column": 7,
569 | "length": 2
570 | }
571 | },
572 | {
573 | "severity": "convention",
574 | "message": "Method has too many lines. [26/20]",
575 | "cop_name": "Metrics/MethodLength",
576 | "corrected": null,
577 | "location": {
578 | "line": 79,
579 | "column": 5,
580 | "length": 3
581 | }
582 | },
583 | {
584 | "severity": "convention",
585 | "message": "Missing space after #.",
586 | "cop_name": "Style/LeadingCommentSpace",
587 | "corrected": false,
588 | "location": {
589 | "line": 80,
590 | "column": 7,
591 | "length": 71
592 | }
593 | },
594 | {
595 | "severity": "convention",
596 | "message": "Missing space after #.",
597 | "cop_name": "Style/LeadingCommentSpace",
598 | "corrected": false,
599 | "location": {
600 | "line": 81,
601 | "column": 7,
602 | "length": 61
603 | }
604 | },
605 | {
606 | "severity": "convention",
607 | "message": "Redundant curly braces around a hash parameter.",
608 | "cop_name": "Style/BracesAroundHashParameters",
609 | "corrected": false,
610 | "location": {
611 | "line": 82,
612 | "column": 42,
613 | "length": 22
614 | }
615 | },
616 | {
617 | "severity": "convention",
618 | "message": "Space inside { missing.",
619 | "cop_name": "Style/SpaceInsideHashLiteralBraces",
620 | "corrected": false,
621 | "location": {
622 | "line": 82,
623 | "column": 42,
624 | "length": 1
625 | }
626 | },
627 | {
628 | "severity": "convention",
629 | "message": "Space inside } missing.",
630 | "cop_name": "Style/SpaceInsideHashLiteralBraces",
631 | "corrected": false,
632 | "location": {
633 | "line": 82,
634 | "column": 63,
635 | "length": 1
636 | }
637 | },
638 | {
639 | "severity": "warning",
640 | "message": "Unused block argument - `event`. You can omit the argument if you don't care about it.",
641 | "cop_name": "Lint/UnusedBlockArgument",
642 | "corrected": false,
643 | "location": {
644 | "line": 84,
645 | "column": 23,
646 | "length": 5
647 | }
648 | },
649 | {
650 | "severity": "convention",
651 | "message": "Do not write to stdout. Use Rails' logger if you want to log.",
652 | "cop_name": "Rails/Output",
653 | "corrected": null,
654 | "location": {
655 | "line": 85,
656 | "column": 9,
657 | "length": 1
658 | }
659 | },
660 | {
661 | "severity": "convention",
662 | "message": "Do not write to stdout. Use Rails' logger if you want to log.",
663 | "cop_name": "Rails/Output",
664 | "corrected": null,
665 | "location": {
666 | "line": 89,
667 | "column": 9,
668 | "length": 1
669 | }
670 | },
671 | {
672 | "severity": "convention",
673 | "message": "`Hash#has_key?` is deprecated in favor of `Hash#key?`.",
674 | "cop_name": "Style/DeprecatedHashMethods",
675 | "corrected": false,
676 | "location": {
677 | "line": 96,
678 | "column": 16,
679 | "length": 8
680 | }
681 | },
682 | {
683 | "severity": "convention",
684 | "message": "Space found before comma.",
685 | "cop_name": "Style/SpaceBeforeComma",
686 | "corrected": false,
687 | "location": {
688 | "line": 100,
689 | "column": 53,
690 | "length": 1
691 | }
692 | },
693 | {
694 | "severity": "convention",
695 | "message": "Space found before comma.",
696 | "cop_name": "Style/SpaceBeforeComma",
697 | "corrected": false,
698 | "location": {
699 | "line": 100,
700 | "column": 57,
701 | "length": 1
702 | }
703 | },
704 | {
705 | "severity": "convention",
706 | "message": "Use the new Ruby 1.9 hash syntax.",
707 | "cop_name": "Style/HashSyntax",
708 | "corrected": false,
709 | "location": {
710 | "line": 102,
711 | "column": 13,
712 | "length": 8
713 | }
714 | },
715 | {
716 | "severity": "convention",
717 | "message": "Use the new Ruby 1.9 hash syntax.",
718 | "cop_name": "Style/HashSyntax",
719 | "corrected": false,
720 | "location": {
721 | "line": 103,
722 | "column": 13,
723 | "length": 8
724 | }
725 | },
726 | {
727 | "severity": "convention",
728 | "message": "Space missing to the left of {.",
729 | "cop_name": "Style/SpaceBeforeBlockBraces",
730 | "corrected": false,
731 | "location": {
732 | "line": 103,
733 | "column": 30,
734 | "length": 1
735 | }
736 | },
737 | {
738 | "severity": "convention",
739 | "message": "Space between { and | missing.",
740 | "cop_name": "Style/SpaceInsideBlockBraces",
741 | "corrected": false,
742 | "location": {
743 | "line": 103,
744 | "column": 30,
745 | "length": 2
746 | }
747 | },
748 | {
749 | "severity": "convention",
750 | "message": "Space missing inside }.",
751 | "cop_name": "Style/SpaceInsideBlockBraces",
752 | "corrected": false,
753 | "location": {
754 | "line": 103,
755 | "column": 48,
756 | "length": 1
757 | }
758 | },
759 | {
760 | "severity": "convention",
761 | "message": "Redundant `return` detected.",
762 | "cop_name": "Style/RedundantReturn",
763 | "corrected": false,
764 | "location": {
765 | "line": 107,
766 | "column": 7,
767 | "length": 6
768 | }
769 | }
770 | ]
771 | },
772 | {
773 | "path": "app/models/application_record.rb",
774 | "offenses": []
775 | },
776 | {
777 | "path": "bin/bundle",
778 | "offenses": []
779 | },
780 | {
781 | "path": "bin/rails",
782 | "offenses": []
783 | },
784 | {
785 | "path": "bin/rake",
786 | "offenses": []
787 | },
788 | {
789 | "path": "bin/setup",
790 | "offenses": [
791 | {
792 | "severity": "convention",
793 | "message": "Use `||` instead of `or`.",
794 | "cop_name": "Style/AndOr",
795 | "corrected": false,
796 | "location": {
797 | "line": 19,
798 | "column": 26,
799 | "length": 2
800 | }
801 | }
802 | ]
803 | },
804 | {
805 | "path": "bin/spring",
806 | "offenses": []
807 | },
808 | {
809 | "path": "bin/update",
810 | "offenses": [
811 | {
812 | "severity": "convention",
813 | "message": "Use `||` instead of `or`.",
814 | "cop_name": "Style/AndOr",
815 | "corrected": false,
816 | "location": {
817 | "line": 19,
818 | "column": 25,
819 | "length": 2
820 | }
821 | }
822 | ]
823 | },
824 | {
825 | "path": "config/application.rb",
826 | "offenses": [
827 | {
828 | "severity": "convention",
829 | "message": "Missing space after #.",
830 | "cop_name": "Style/LeadingCommentSpace",
831 | "corrected": false,
832 | "location": {
833 | "line": 14,
834 | "column": 5,
835 | "length": 27
836 | }
837 | },
838 | {
839 | "severity": "convention",
840 | "message": "Missing space after #.",
841 | "cop_name": "Style/LeadingCommentSpace",
842 | "corrected": false,
843 | "location": {
844 | "line": 15,
845 | "column": 5,
846 | "length": 41
847 | }
848 | }
849 | ]
850 | },
851 | {
852 | "path": "config/boot.rb",
853 | "offenses": []
854 | },
855 | {
856 | "path": "config/environment.rb",
857 | "offenses": []
858 | },
859 | {
860 | "path": "config/environments/development.rb",
861 | "offenses": []
862 | },
863 | {
864 | "path": "config/environments/production.rb",
865 | "offenses": [
866 | {
867 | "severity": "convention",
868 | "message": "Space inside square brackets detected.",
869 | "cop_name": "Style/SpaceInsideBrackets",
870 | "corrected": false,
871 | "location": {
872 | "line": 53,
873 | "column": 22,
874 | "length": 1
875 | }
876 | },
877 | {
878 | "severity": "convention",
879 | "message": "Space inside square brackets detected.",
880 | "cop_name": "Style/SpaceInsideBrackets",
881 | "corrected": false,
882 | "location": {
883 | "line": 53,
884 | "column": 34,
885 | "length": 1
886 | }
887 | }
888 | ]
889 | },
890 | {
891 | "path": "config/environments/test.rb",
892 | "offenses": []
893 | },
894 | {
895 | "path": "config/initializers/active_record_belongs_to_required_by_default.rb",
896 | "offenses": []
897 | },
898 | {
899 | "path": "config/initializers/application_controller_renderer.rb",
900 | "offenses": []
901 | },
902 | {
903 | "path": "config/initializers/assets.rb",
904 | "offenses": []
905 | },
906 | {
907 | "path": "config/initializers/backtrace_silencers.rb",
908 | "offenses": []
909 | },
910 | {
911 | "path": "config/initializers/callback_terminator.rb",
912 | "offenses": []
913 | },
914 | {
915 | "path": "config/initializers/cookies_serializer.rb",
916 | "offenses": []
917 | },
918 | {
919 | "path": "config/initializers/filter_parameter_logging.rb",
920 | "offenses": []
921 | },
922 | {
923 | "path": "config/initializers/inflections.rb",
924 | "offenses": []
925 | },
926 | {
927 | "path": "config/initializers/mime_types.rb",
928 | "offenses": []
929 | },
930 | {
931 | "path": "config/initializers/per_form_csrf_tokens.rb",
932 | "offenses": []
933 | },
934 | {
935 | "path": "config/initializers/redis.rb",
936 | "offenses": [
937 | {
938 | "severity": "convention",
939 | "message": "Use the new Ruby 1.9 hash syntax.",
940 | "cop_name": "Style/HashSyntax",
941 | "corrected": false,
942 | "location": {
943 | "line": 1,
944 | "column": 20,
945 | "length": 8
946 | }
947 | },
948 | {
949 | "severity": "convention",
950 | "message": "Use the new Ruby 1.9 hash syntax.",
951 | "cop_name": "Style/HashSyntax",
952 | "corrected": false,
953 | "location": {
954 | "line": 1,
955 | "column": 65,
956 | "length": 8
957 | }
958 | }
959 | ]
960 | },
961 | {
962 | "path": "config/initializers/request_forgery_protection.rb",
963 | "offenses": []
964 | },
965 | {
966 | "path": "config/initializers/session_store.rb",
967 | "offenses": []
968 | },
969 | {
970 | "path": "config/initializers/wrap_parameters.rb",
971 | "offenses": []
972 | },
973 | {
974 | "path": "config/puma.rb",
975 | "offenses": [
976 | {
977 | "severity": "convention",
978 | "message": "Prefer single-quoted strings when you don't need string interpolation or special symbols.",
979 | "cop_name": "Style/StringLiterals",
980 | "corrected": false,
981 | "location": {
982 | "line": 7,
983 | "column": 27,
984 | "length": 19
985 | }
986 | },
987 | {
988 | "severity": "convention",
989 | "message": "Prefer single-quoted strings when you don't need string interpolation or special symbols.",
990 | "cop_name": "Style/StringLiterals",
991 | "corrected": false,
992 | "location": {
993 | "line": 12,
994 | "column": 23,
995 | "length": 6
996 | }
997 | },
998 | {
999 | "severity": "convention",
1000 | "message": "Prefer single-quoted strings when you don't need string interpolation or special symbols.",
1001 | "cop_name": "Style/StringLiterals",
1002 | "corrected": false,
1003 | "location": {
1004 | "line": 16,
1005 | "column": 23,
1006 | "length": 11
1007 | }
1008 | },
1009 | {
1010 | "severity": "convention",
1011 | "message": "Prefer single-quoted strings when you don't need string interpolation or special symbols.",
1012 | "cop_name": "Style/StringLiterals",
1013 | "corrected": false,
1014 | "location": {
1015 | "line": 16,
1016 | "column": 38,
1017 | "length": 13
1018 | }
1019 | }
1020 | ]
1021 | },
1022 | {
1023 | "path": "config/routes.rb",
1024 | "offenses": []
1025 | },
1026 | {
1027 | "path": "test/controllers/logging_controller_test.rb",
1028 | "offenses": [
1029 | {
1030 | "severity": "convention",
1031 | "message": "Prefer single-quoted strings when you don't need string interpolation or special symbols.",
1032 | "cop_name": "Style/StringLiterals",
1033 | "corrected": false,
1034 | "location": {
1035 | "line": 4,
1036 | "column": 8,
1037 | "length": 18
1038 | }
1039 | },
1040 | {
1041 | "severity": "convention",
1042 | "message": "Extra empty line detected at class body end.",
1043 | "cop_name": "Style/EmptyLinesAroundClassBody",
1044 | "corrected": false,
1045 | "location": {
1046 | "line": 8,
1047 | "column": 1,
1048 | "length": 1
1049 | }
1050 | }
1051 | ]
1052 | },
1053 | {
1054 | "path": "test/test_helper.rb",
1055 | "offenses": [
1056 | {
1057 | "severity": "convention",
1058 | "message": "Use nested module/class definitions instead of compact style.",
1059 | "cop_name": "Style/ClassAndModuleChildren",
1060 | "corrected": null,
1061 | "location": {
1062 | "line": 5,
1063 | "column": 7,
1064 | "length": 23
1065 | }
1066 | }
1067 | ]
1068 | }
1069 | ],
1070 | "summary": {
1071 | "offense_count": 79,
1072 | "target_file_count": 44,
1073 | "inspected_file_count": 44
1074 | }
1075 | }
--------------------------------------------------------------------------------
/src/test/resources/test_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | # Prevent CSRF attacks by raising an exception.
3 | # For APIs, you may want to use :null_session instead.
4 | protect_from_forgery with: :exception
5 | # before_action :login
6 | before_action :authenticate_user!
7 | # helper_method :current_user
8 | # def login
9 | # @user = User.new
10 | # # @user.role = params[:fake_role].blank? ? :client : params[:fake_role].to_sym
11 | # end
12 | # def current_user
13 | # @user
14 | # end
15 | end
--------------------------------------------------------------------------------
/src/test/resources/unknown_file.rb:
--------------------------------------------------------------------------------
1 | puts "I am an unknown file"
2 |
3 | # This is a comment
4 |
5 | puts "Just trying to get to 10 lines"
6 |
7 | a = 1 + 2
8 | puts a
9 | b = !false
10 | puts "It's funny cause it's true"
--------------------------------------------------------------------------------