diff --git a/.rubocop.yml b/.rubocop.yml index eaba5caa6..ee3c1ccae 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,14 +1,20 @@ # This is the configuration used to check the rubocop source code. +# Check out: https://github.com/bbatsov/rubocop AllCops: + Include: + - '**/Rakefile' + - '**/config.ru' Exclude: - 'vendor/**/*' - 'spec/fixtures/**/*' - 'node_modules/**/*' + - 'db/**/*' - 'db/schema.rb' - 'db/seeds.rb' - 'client/node_modules/**/*' - 'bin/**/*' + - !ruby/regexp /old_and_unused\.rb$/ Metrics/LineLength: Max: 120 diff --git a/.scss-lint.yml b/.scss-lint.yml new file mode 100644 index 000000000..52b2a8544 --- /dev/null +++ b/.scss-lint.yml @@ -0,0 +1,205 @@ +# See http://sass-guidelin.es/#zeros + +scss_files: + - 'app/assets/stylesheets/**/*.scss' + - 'client/assets/stylesheets/**/*.scss' + +linters: +# BangFormat: +# enabled: true +# space_before_bang: true +# space_after_bang: false +# +# BorderZero: +# enabled: true +# convention: zero # or `none` +# + ColorKeyword: + enabled: false + ColorVariable: + enabled: false +# +# Comment: +# enabled: true +# +# DebugStatement: +# enabled: true +# +# DeclarationOrder: +# enabled: true +# +# DuplicateProperty: +# enabled: true +# +# ElsePlacement: +# enabled: true +# style: same_line # or 'new_line' +# +# EmptyLineBetweenBlocks: +# enabled: true +# ignore_single_line_blocks: true +# +# EmptyRule: +# enabled: true +# +# FinalNewline: +# enabled: true +# present: true +# + HexLength: + enabled: true + style: long + + HexNotation: + enabled: true + style: uppercase +# +# HexValidation: +# enabled: true +# + IdSelector: + enabled: true +# +# ImportantRule: +# enabled: true +# +# ImportPath: +# enabled: true +# leading_underscore: false +# filename_extension: false +# +# Indentation: +# enabled: true +# allow_non_nested_indentation: false +# character: space # or 'tab' +# width: 2 +# + LeadingZero: + enabled: true + style: include_zero +# +# MergeableSelector: +# enabled: true +# force_nesting: true +# +# NameFormat: +# enabled: true +# allow_leading_underscore: true +# convention: hyphenated_lowercase # or 'camel_case', or 'snake_case', or a regex pattern +# +# NestingDepth: +# enabled: true +# max_depth: 3 +# +# PlaceholderInExtend: +# enabled: true +# +# PropertyCount: +# enabled: false +# include_nested: false +# max_properties: 10 +# +# PropertyUnits: +# enabled: true +# global: [ +# 'ch', 'em', 'ex', 'rem', # Font-relative lengths +# 'cm', 'in', 'mm', 'pc', 'pt', 'px', 'q', # Absolute lengths +# 'vh', 'vw', 'vmin', 'vmax', # Viewport-percentage lengths +# 'deg', 'grad', 'rad', 'turn', # Angle +# 'ms', 's', # Duration +# 'Hz', 'kHz', # Frequency +# 'dpi', 'dpcm', 'dppx', # Resolution +# '%', # Other +# ] +# properties: {} +# +# PropertySortOrder: +# enabled: true +# ignore_unspecified: false +# separate_groups: false +# +# PropertySpelling: +# enabled: true +# extra_properties: [] +# +# QualifyingElement: +# enabled: true +# allow_element_with_attribute: false +# allow_element_with_class: false +# allow_element_with_id: false +# +# SelectorDepth: +# enabled: true +# max_depth: 3 +# +# SelectorFormat: +# enabled: true +# convention: hyphenated_lowercase # or 'strict_BEM', or 'hyphenated_BEM', or 'snake_case', or 'camel_case', or a regex pattern +# +# Shorthand: +# enabled: true +# allowed_shorthands: [1, 2, 3] +# +# SingleLinePerProperty: +# enabled: true +# allow_single_line_rule_sets: true +# +# SingleLinePerSelector: +# enabled: true +# +# SpaceAfterComma: +# enabled: true +# +# SpaceAfterPropertyColon: +# enabled: true +# style: one_space # or 'no_space', or 'at_least_one_space', or 'aligned' +# +# SpaceAfterPropertyName: +# enabled: true +# +# SpaceBeforeBrace: +# enabled: true +# style: space # or 'new_line' +# allow_single_line_padding: false +# +# SpaceBetweenParens: +# enabled: true +# spaces: 0 +# +# StringQuotes: +# enabled: true +# style: single_quotes # or double_quotes +# +# TrailingSemicolon: +# enabled: true +# +# TrailingZero: +# enabled: false +# +# UnnecessaryMantissa: +# enabled: true +# +# UnnecessaryParentReference: +# enabled: true +# +# UrlFormat: +# enabled: true +# +# UrlQuotes: +# enabled: true +# +# VariableForProperty: +# enabled: false +# properties: [] +# +# VendorPrefix: +# enabled: true +# identifier_list: base +# additional_identifiers: [] +# excluded_identifiers: [] +# +# ZeroUnit: +# enabled: true +# +# Compass::*: +# enabled: false diff --git a/Gemfile b/Gemfile index d8dcd0678..49c799d7d 100644 --- a/Gemfile +++ b/Gemfile @@ -62,6 +62,12 @@ group :development, :test do gem "rubocop", require: false gem "ruby-lint", require: false + + gem "scss-lint", require: false + + gem "brakeman", require: false + + gem "bundler-audit", require: false end gem "spring-commands-rspec", group: :development diff --git a/Gemfile.lock b/Gemfile.lock index eb46e9690..eeb18b19e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -49,7 +49,20 @@ GEM bootstrap-sass (3.3.4.1) autoprefixer-rails (>= 5.0.0.1) sass (>= 3.2.19) + brakeman (3.0.3) + erubis (~> 2.6) + fastercsv (~> 1.5) + haml (>= 3.0, < 5.0) + highline (~> 1.6.20) + multi_json (~> 1.2) + ruby2ruby (~> 2.1.1) + ruby_parser (~> 3.6.2) + sass (~> 3.0) + terminal-table (~> 1.4) builder (3.2.2) + bundler-audit (0.3.1) + bundler (~> 1.2) + thor (~> 0.18) byebug (4.0.3) columnize (= 0.9.0) capybara (2.4.4) @@ -82,10 +95,14 @@ GEM factory_girl_rails (4.5.0) factory_girl (~> 4.5.0) railties (>= 3.0.0) + fastercsv (1.5.5) foreman (0.78.0) thor (~> 0.19.1) globalid (0.3.3) activesupport (>= 4.1.0) + haml (4.0.6) + tilt + highline (1.6.21) hike (1.2.3) i18n (0.7.0) jbuilder (2.2.12) @@ -177,6 +194,11 @@ GEM parser (~> 2.1, >= 2.1.1) slop (~> 3.4, >= 3.4.7) ruby-progressbar (1.7.5) + ruby2ruby (2.1.4) + ruby_parser (~> 3.1) + sexp_processor (~> 4.0) + ruby_parser (3.6.6) + sexp_processor (~> 4.1) sass (3.4.13) sass-rails (5.0.1) railties (>= 4.0.0, < 5.0) @@ -184,9 +206,13 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (~> 1.1) + scss-lint (0.37.0) + rainbow (~> 2.0) + sass (~> 3.4.1) sdoc (0.4.1) json (~> 1.7, >= 1.7.7) rdoc (~> 4.0) + sexp_processor (4.5.1) slop (3.6.0) spring (1.3.3) spring-commands-rspec (1.0.4) @@ -201,6 +227,7 @@ GEM activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) sqlite3 (1.3.10) + terminal-table (1.4.5) thor (0.19.1) thread_safe (0.3.5) tilt (1.4.1) @@ -229,6 +256,8 @@ PLATFORMS DEPENDENCIES autoprefixer-rails bootstrap-sass (~> 3.3.1) + brakeman + bundler-audit byebug capybara capybara-screenshot @@ -248,6 +277,7 @@ DEPENDENCIES rubocop ruby-lint sass-rails + scss-lint sdoc spring spring-commands-rspec diff --git a/README.md b/README.md index 031c1970c..9e5088d95 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ In no particular order: - Easily enable use of npm modules with a Rails application. - Easily enable retrofitting such a JS framework into an existing Rails app. - Enable the use of the JavaScript ES6 transpiler. +- Example setting up Ruby and ES6 linting in a real project. # Technologies involved @@ -216,18 +217,6 @@ To deploy the app on Heroku: git push heroku master ``` -# Update Node Modules -``` -rm npm-shrinkwrap.json -npm-check-updates -u -npm install -npm shrinkwrap -``` - -Then confirm that the hot reload server and the rails server both work fine. You -may have to delete `node_modules` and `npm-shrinkwrap.json` and then run `npm -shrinkwrap`. - # Running Tests *Default rake task runs tests and linting* @@ -258,14 +247,26 @@ QMAKE=/usr/local/Cellar/qt5/5.4.0/bin/qmake bundle install Then run `rspec` and you should see the tests have passed. # Linting and Code Inspection -* Default rake task runs tests and linting (yes, repeating this!) +## Running Lint and CI tasks +* Default rake task runs tests and linting (yes, repeating this!) (see `ci.rake`) * See file [README.md](client/README.md) for how to run ESLint and JSCS +* See scripts `scripts/lint` and `client/bin/lint`. * Create a custom scope like this for RubyMine, named "Inspection Scope" file[react-rails-tutorial]:*/&&!file[react-rails-tutorial]:tmp//*&&!file[react-rails-tutorial]:log//*&&!file[react-rails-tutorial]:client/node_modules//*&&!file[react-rails-tutorial]:client/assets/fonts//*&&!file[react-rails-tutorial]:app/assets/fonts//*&&!file[react-rails-tutorial]:bin//*&&!file[react-rails-tutorial]:app/assets/javascripts//* * Install the code style and inspection files in [client/jetbrains](client/jetbrains) * Use the installed inspection settings and new Inspection Scope for code inspection. +* RubyMine configuration is optional. All linters run from the command line. + +## Linters + 1. [Rubocop](https://github.com/bbatsov/rubocop) + 2. [Ruby-Lint](https://github.com/YorickPeterse/ruby-lint) + 3. [Eslint](http://eslint.org/) + 4. [JSCS](https://github.com/jscs-dev/node-jscs) + 5. [scss-lint](https://github.com/brigade/scss-lint) + 6. [brakeman](http://brakemanscanner.org/) + 7. [bundle-audit](https://github.com/rubysec/bundler-audit) # Contributors * [Martin Breining](https://github.com/mbreining) diff --git a/app/assets/stylesheets/_bootstrap-custom.scss b/app/assets/stylesheets/_bootstrap-custom.scss index b987dca15..f2708cb79 100644 --- a/app/assets/stylesheets/_bootstrap-custom.scss +++ b/app/assets/stylesheets/_bootstrap-custom.scss @@ -2,58 +2,58 @@ // The _bootstrap-variables-customization.scss file is located under // client/assets/stylesheets, which has been added to the Rails asset // pipeline search path. See config/application.rb. -@import "bootstrap-variables-customization"; +@import 'bootstrap-variables-customization'; // Core variables and mixins -@import "bootstrap/variables"; -@import "bootstrap/mixins"; +@import 'bootstrap/variables'; +@import 'bootstrap/mixins'; // Reset and dependencies -@import "bootstrap/normalize"; -@import "bootstrap/print"; -@import "bootstrap/glyphicons"; +@import 'bootstrap/normalize'; +@import 'bootstrap/print'; +@import 'bootstrap/glyphicons'; // Core CSS -@import "bootstrap/scaffolding"; -@import "bootstrap/type"; -@import "bootstrap/code"; -@import "bootstrap/grid"; -@import "bootstrap/tables"; -@import "bootstrap/forms"; -@import "bootstrap/buttons"; +@import 'bootstrap/scaffolding'; +@import 'bootstrap/type'; +@import 'bootstrap/code'; +@import 'bootstrap/grid'; +@import 'bootstrap/tables'; +@import 'bootstrap/forms'; +@import 'bootstrap/buttons'; // Components -@import "bootstrap/component-animations"; -@import "bootstrap/dropdowns"; -@import "bootstrap/button-groups"; -@import "bootstrap/input-groups"; -@import "bootstrap/navs"; -@import "bootstrap/navbar"; -@import "bootstrap/breadcrumbs"; -@import "bootstrap/pagination"; -@import "bootstrap/pager"; -@import "bootstrap/labels"; -@import "bootstrap/badges"; -//@import "bootstrap/jumbotron"; // excluding as an example -@import "bootstrap/thumbnails"; -@import "bootstrap/alerts"; -//@import "bootstrap/progress-bars"; // excluding as an example -@import "bootstrap/media"; -@import "bootstrap/list-group"; -@import "bootstrap/panels"; -@import "bootstrap/responsive-embed"; -@import "bootstrap/wells"; -@import "bootstrap/close"; +@import 'bootstrap/component-animations'; +@import 'bootstrap/dropdowns'; +@import 'bootstrap/button-groups'; +@import 'bootstrap/input-groups'; +@import 'bootstrap/navs'; +@import 'bootstrap/navbar'; +@import 'bootstrap/breadcrumbs'; +@import 'bootstrap/pagination'; +@import 'bootstrap/pager'; +@import 'bootstrap/labels'; +@import 'bootstrap/badges'; +//@import 'bootstrap/jumbotron'; // excluding as an example +@import 'bootstrap/thumbnails'; +@import 'bootstrap/alerts'; +//@import 'bootstrap/progress-bars'; // excluding as an example +@import 'bootstrap/media'; +@import 'bootstrap/list-group'; +@import 'bootstrap/panels'; +@import 'bootstrap/responsive-embed'; +@import 'bootstrap/wells'; +@import 'bootstrap/close'; // Components w/ JavaScript -@import "bootstrap/modals"; // excluding as an example -@import "bootstrap/tooltip"; -@import "bootstrap/popovers"; -@import "bootstrap/carousel"; // excluding as an example +@import 'bootstrap/modals'; // excluding as an example +@import 'bootstrap/tooltip'; +@import 'bootstrap/popovers'; +@import 'bootstrap/carousel'; // excluding as an example // Utility classes -@import "bootstrap/utilities"; -@import "bootstrap/responsive-utilities"; +@import 'bootstrap/utilities'; +@import 'bootstrap/responsive-utilities'; // This must come after all the boostrap styles are loaded so that these styles can override those. -@import "app-styling-post-bootstrap-loading"; +@import 'app-styling-post-bootstrap-loading'; diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 05c057045..e26701236 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -5,8 +5,8 @@ $rails: true; // Those scss files are located under client/assets/stylesheets, // which has been added to the Rails asset pipeline search path. // See config/application.rb. -@import "bootstrap-custom"; -@import "test-stylesheet"; -@import "test-sass-stylesheet"; +@import 'bootstrap-custom'; +@import 'test-stylesheet'; +@import 'test-sass-stylesheet'; -@import "bootstrap-sprockets"; +@import 'bootstrap-sprockets'; diff --git a/app/assets/stylesheets/scaffolds.css.scss b/app/assets/stylesheets/scaffolds.css.scss index f0f9f3bd6..5bb34c9ae 100644 --- a/app/assets/stylesheets/scaffolds.css.scss +++ b/app/assets/stylesheets/scaffolds.css.scss @@ -1,36 +1,43 @@ +// scss-lint:disable SelectorFormat, IdSelector body { - background-color: #fff; - color: #333; + background-color: #FFFFFF; + color: #333333; font-family: verdana, arial, helvetica, sans-serif; font-size: 13px; line-height: 18px; } -p, ol, ul, td { +p, +ol, +ul, +td { font-family: verdana, arial, helvetica, sans-serif; font-size: 13px; line-height: 18px; } pre { - background-color: #eee; - padding: 10px; + background-color: #EEEEEE; font-size: 11px; + padding: 10px; } a { - color: #000; + color: #000000; + &:visited { - color: #666; + color: #666666; } + &:hover { - color: #fff; - background-color: #000; + background-color: #000000; + color: #FFFFFF; } } div { - &.field, &.actions { + &.field, + &.actions { margin-bottom: 10px; } } @@ -40,27 +47,29 @@ div { } .field_with_errors { - padding: 2px; background-color: red; display: table; + padding: 2px; } #error_explanation { - width: 450px; + background-color: #F0F0F0; border: 2px solid red; - padding: 7px 7px 0; margin-bottom: 20px; - background-color: #f0f0f0; + padding: 7px 7px 0; + width: 450px; + h2 { - text-align: left; - font-weight: bold; - padding: 5px 5px 5px 15px; + background-color: #CC0000; + color: #FFFFFF; font-size: 12px; + font-weight: bold; margin: -7px; margin-bottom: 0; - background-color: #c00; - color: #fff; + padding: 5px 5px 5px 15px; + text-align: left; } + ul li { font-size: 12px; list-style: square; diff --git a/client/.eslintrc b/client/.eslintrc index f2f6f2521..4f2691a78 100644 --- a/client/.eslintrc +++ b/client/.eslintrc @@ -163,7 +163,12 @@ "no-wrap-func": 2, // http://eslint.org/docs/rules/no-wrap-func "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var - "padded-blocks": [1, "never"], // http://eslint.org/docs/rules/padded-blocks + + # Change after this issue is fixed: + # https://github.com/babel/babel-eslint/issues/33 + "padded-blocks": 0, // http://eslint.org/docs/rules/padded-blocks +# "padded-blocks": [1, "never"], // http://eslint.org/docs/rules/padded-blocks + "semi": [2, "always"], // http://eslint.org/docs/rules/semi "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing "before": false, diff --git a/client/README.md b/client/README.md index 0c959a129..df88be487 100644 --- a/client/README.md +++ b/client/README.md @@ -20,3 +20,28 @@ So don't use `npm run gulp lint` yet. For now: bin/lint + + + +Updating Node Dependenencies +=========================== + +``` +npm install -g npm-check-updates +``` + + +``` +rm npm-shrinkwrap.json +npm-check-updates -u +npm install +npm shrinkwrap +``` + +Then confirm that the hot reload server and the rails server both work fine. You +may have to delete `node_modules` and `npm-shrinkwrap.json` and then run `npm +shrinkwrap`. + + + + diff --git a/client/assets/javascripts/FluxAlt.js b/client/assets/javascripts/FluxAlt.js index 396d6ca9b..4575c21ec 100644 --- a/client/assets/javascripts/FluxAlt.js +++ b/client/assets/javascripts/FluxAlt.js @@ -1,5 +1,3 @@ -'use strict'; - import Alt from 'alt'; const alt = new Alt(); diff --git a/client/assets/javascripts/actions/FormActions.js b/client/assets/javascripts/actions/FormActions.js index 966a90c76..507e3fcd9 100644 --- a/client/assets/javascripts/actions/FormActions.js +++ b/client/assets/javascripts/actions/FormActions.js @@ -22,8 +22,8 @@ class FormActions { submitComment(url, comment) { this.dispatch(); CommentsManager.submitComment(url, comment) - .then((comment) => { - CommentActions.addComment(comment); + .then((returnedComment) => { + CommentActions.addComment(returnedComment); }, (errorMessage) => { diff --git a/client/assets/javascripts/components/CommentBox.jsx b/client/assets/javascripts/components/CommentBox.jsx index 46b2b46e1..82af0fd68 100644 --- a/client/assets/javascripts/components/CommentBox.jsx +++ b/client/assets/javascripts/components/CommentBox.jsx @@ -1,11 +1,9 @@ -import $ from 'jquery'; import React from 'react'; import CommentForm from './CommentForm'; import CommentList from './CommentList'; import CommentStore from '../stores/CommentStore'; import FormStore from '../stores/FormStore'; import CommentActions from '../actions/CommentActions'; -import FormActions from '../actions/FormActions'; const CommentBox = React.createClass({ displayName: 'CommentBox', diff --git a/client/assets/javascripts/components/CommentForm.jsx b/client/assets/javascripts/components/CommentForm.jsx index 2bd0e4dd1..7a8ffc9fa 100644 --- a/client/assets/javascripts/components/CommentForm.jsx +++ b/client/assets/javascripts/components/CommentForm.jsx @@ -16,29 +16,30 @@ const CommentForm = React.createClass({ ajaxSending: React.PropTypes.bool.isRequired }, - getInitialState: function() { + getInitialState() { return { formMode: 0 }; }, - handleSelect: function(selectedKey) { + handleSelect(selectedKey) { this.setState({formMode: selectedKey}); }, handleChange() { + let obj; + // This could also be done using ReactLink: // http://facebook.github.io/react/docs/two-way-binding-helpers.html - let obj; if (this.state.formMode < 2) { obj = { author: this.refs.author.getValue(), text: this.refs.text.getValue() }; } else { - // This is different because the input is a native HTML element - // rather than a React element. obj = { + // This is different because the input is a native HTML element + // rather than a React element. author: this.refs.inlineAuthor.getDOMNode().value, text: this.refs.inlineText.getDOMNode().value }; @@ -52,7 +53,7 @@ const CommentForm = React.createClass({ FormActions.submitComment(this.props.url, FormStore.getState().comment); }, - formHorizontal: function() { + formHorizontal() { return (