From 4ba210cf8102f2073c1ebc7062bd211c266ab46f Mon Sep 17 00:00:00 2001 From: deppbot Date: Sun, 24 Dec 2017 08:09:48 +0800 Subject: [PATCH 001/219] Bundle Update on 2017-12-24 --- Gemfile.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index b9a7ee954..31113ea38 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -181,8 +181,8 @@ GEM factory_bot_rails (4.8.2) factory_bot (~> 4.8.2) railties (>= 3.0.0) - faker (1.8.5) - i18n (~> 0.9.1) + faker (1.8.7) + i18n (>= 0.7) faraday (0.12.2) multipart-post (>= 1.2, < 3) ffi (1.9.18) @@ -238,7 +238,7 @@ GEM rake (>= 10, < 13) rubocop (>= 0.49.0) sysexits (~> 1.1) - hashie (3.5.6) + hashie (3.5.7) heroics (0.0.24) erubis (~> 2.0) excon @@ -276,7 +276,7 @@ GEM thor (>= 0.14, < 2.0) jquery-ui-rails (5.0.5) railties (>= 3.2.16) - js-routes (1.4.2) + js-routes (1.4.3) railties (>= 3.2) sprockets-rails json (2.1.0) @@ -329,7 +329,7 @@ GEM multi_xml (0.6.0) multipart-post (2.0.0) nenv (0.3.0) - newrelic_rpm (4.6.0.338) + newrelic_rpm (4.7.1.340) nokogiri (1.8.1) mini_portile2 (~> 2.3.0) notiffany (0.1.1) @@ -408,8 +408,8 @@ GEM rails-assets-leaflet (>= 1.0.3) rails-deprecated_sanitizer (1.0.3) activesupport (>= 4.2.0.alpha) - rails-dom-testing (1.0.8) - activesupport (>= 4.2.0.beta, < 5.0) + rails-dom-testing (1.0.9) + activesupport (>= 4.2.0, < 5.0) nokogiri (~> 1.6) rails-deprecated_sanitizer (>= 1.0.1) rails-html-sanitizer (1.0.3) @@ -528,7 +528,7 @@ GEM uglifier (4.0.2) execjs (>= 0.3.0, < 3) unicode-display_width (1.3.0) - unicorn (5.3.1) + unicorn (5.4.0) kgio (~> 2.6) raindrops (~> 0.7) uniform_notifier (1.10.0) From 93eba14af976c1ac4d0342091f10eab3ad0e7b5c Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 11:26:08 +1300 Subject: [PATCH 002/219] Adding package lock, and eslint --- .eslintrc.js | 3 + .overcommit.yml | 8 +- package-lock.json | 814 ++++++++++++++++++++++------------------------ package.json | 3 +- 4 files changed, 400 insertions(+), 428 deletions(-) create mode 100644 .eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..f2ddd9cea --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + "extends": "google" +}; \ No newline at end of file diff --git a/.overcommit.yml b/.overcommit.yml index 505c8e2e6..d7899885f 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -46,7 +46,9 @@ PreCommit: required_executable: 'npm' HamlLint: enabled: true - command: ['bundle', 'exec', 'haml-lint', 'app/views'] + include: + - 'app/views/**' + command: ['bundle', 'exec', 'haml-lint'] JsonSyntax: enabled: true BundleOutdated: @@ -55,13 +57,13 @@ PreCommit: BundleAudit: enabled: true on_warn: warn - JsHint: + EsLint: enabled: true exclude: - 'app/assets/**' - 'spec/javascripts/support/vendor/**' - '**/bootstrap*' - command: ['npm', 'run', 'jshint'] + command: ['./node_modules/.bin/eslint'] required_executable: 'npm' ScssLint: enabled: true diff --git a/package-lock.json b/package-lock.json index 013886a6b..baf5adb61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "acorn": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.2.1.tgz", - "integrity": "sha512-jG0u7c4Ly+3QkkW18V+NRDN+4bWHdln30NL1ZL2AvFZZmQe/BfopYCtghCKKVBUSetZ4QKcyA0pY6/4Gw8Pv8w==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.3.0.tgz", + "integrity": "sha512-Yej+zOJ1Dm/IMZzzj78OntP/r3zHEaKcyNoU2lAaxPtrseM6rF0xwqoz5Q5ysAiED9hTjI2hgtvLXitlCN1/Ug==", "dev": true }, "acorn-jsx": { @@ -28,25 +28,27 @@ } }, "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "dev": true, "requires": { "co": "4.6.0", - "json-stable-stringify": "1.0.1" + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" } }, "ajv-keywords": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", - "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", "dev": true }, "ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz", + "integrity": "sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ==", "dev": true }, "ansi-regex": { @@ -100,6 +102,30 @@ "chalk": "1.1.3", "esutils": "2.0.2", "js-tokens": "3.0.2" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + } } }, "balanced-match": { @@ -134,22 +160,46 @@ "dev": true }, "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "2.2.1", + "ansi-styles": "3.2.0", "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "supports-color": "4.5.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } } }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, "circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "integrity": "sha1-gVyZ6oT2gJUp0vRXkb34JxE1LWY=", "dev": true }, "cli": { @@ -163,12 +213,12 @@ } }, "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { - "restore-cursor": "1.0.1" + "restore-cursor": "2.0.0" } }, "cli-width": { @@ -189,12 +239,6 @@ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, "coffeelint": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/coffeelint/-/coffeelint-2.0.7.tgz", @@ -212,7 +256,22 @@ "coffeescript": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.0.3.tgz", - "integrity": "sha512-iIfUN+71IyI2FQABXh1luzZeQgqwUPeWh6lDovJatQQs+30bvyGnBY0r4BnD0hoMAasNuZVHlL1U09Oy1ZfSeg==", + "integrity": "sha1-dg8Cck9fCRG+fO+jSo4OEMXYUSo=", + "dev": true + }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha1-wSYRB66y8pTr/+ye2eytUppgl+0=", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, "concat-map": { @@ -247,6 +306,17 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" + } + }, "csslint": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/csslint/-/csslint-1.0.5.tgz", @@ -257,15 +327,6 @@ "parserlib": "1.1.1" } }, - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dev": true, - "requires": { - "es5-ext": "0.10.35" - } - }, "date-now": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", @@ -273,9 +334,9 @@ "dev": true }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -303,13 +364,12 @@ } }, "doctrine": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", - "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.2.tgz", + "integrity": "sha512-y0tm5Pq6ywp3qSTZ1vPgVdAnbDEoeoc5wlOHXoY1c4Wug/a7JvqHIl7BTvwodaHmejWkK/9dSb3sCYfyo/om8A==", "dev": true, "requires": { - "esutils": "2.0.2", - "isarray": "1.0.0" + "esutils": "2.0.2" } }, "dom-serializer": { @@ -367,135 +427,55 @@ "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", "dev": true }, - "es5-ext": { - "version": "0.10.35", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.35.tgz", - "integrity": "sha1-GO6FjOajxFx9eekcFfzKnsVoSU8=", - "dev": true, - "requires": { - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.35", - "es6-symbol": "3.1.1" - } - }, - "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.35", - "es6-iterator": "2.0.3", - "es6-set": "0.1.5", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" - } - }, - "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.35", - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" - } - }, - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.35" - } - }, - "es6-weak-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.35", - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1" - } - }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "escope": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", - "dev": true, - "requires": { - "es6-map": "0.1.5", - "es6-weak-map": "2.0.2", - "esrecurse": "4.2.0", - "estraverse": "4.2.0" - } - }, "eslint": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", - "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.14.0.tgz", + "integrity": "sha512-Ul6CSGRjKscEyg0X/EeNs7o2XdnbTEOD1OM8cTjmx85RPcBJQrEhZLevhuJZNAE/vS2iVl5Uhgiqf3h5uLMCJQ==", "dev": true, "requires": { + "ajv": "5.5.2", "babel-code-frame": "6.26.0", - "chalk": "1.1.3", + "chalk": "2.3.0", "concat-stream": "1.6.0", - "debug": "2.6.9", - "doctrine": "2.0.0", - "escope": "3.6.0", - "espree": "3.5.1", + "cross-spawn": "5.1.0", + "debug": "3.1.0", + "doctrine": "2.0.2", + "eslint-scope": "3.7.1", + "eslint-visitor-keys": "1.0.0", + "espree": "3.5.2", "esquery": "1.0.0", - "estraverse": "4.2.0", "esutils": "2.0.2", "file-entry-cache": "2.0.0", + "functional-red-black-tree": "1.0.1", "glob": "7.1.2", - "globals": "9.18.0", + "globals": "11.1.0", "ignore": "3.3.7", "imurmurhash": "0.1.4", - "inquirer": "0.12.0", - "is-my-json-valid": "2.16.1", - "is-resolvable": "1.0.0", + "inquirer": "3.3.0", + "is-resolvable": "1.0.1", "js-yaml": "3.10.0", - "json-stable-stringify": "1.0.1", + "json-stable-stringify-without-jsonify": "1.0.1", "levn": "0.3.0", "lodash": "4.17.4", + "minimatch": "3.0.4", "mkdirp": "0.5.1", "natural-compare": "1.4.0", "optionator": "0.8.2", "path-is-inside": "1.0.2", - "pluralize": "1.2.1", - "progress": "1.1.8", + "pluralize": "7.0.0", + "progress": "2.0.0", "require-uncached": "1.0.3", - "shelljs": "0.7.8", - "strip-bom": "3.0.0", + "semver": "5.4.1", + "strip-ansi": "4.0.0", "strip-json-comments": "2.0.1", - "table": "3.8.3", - "text-table": "0.2.0", - "user-home": "2.0.0" + "table": "4.0.2", + "text-table": "0.2.0" }, "dependencies": { "strip-json-comments": { @@ -506,20 +486,42 @@ } } }, - "espree": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.1.tgz", - "integrity": "sha1-DJiLirRttTEAoZVK5LqZXd0n2H4=", + "eslint-config-google": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.9.1.tgz", + "integrity": "sha512-5A83D+lH0PA81QMESKbLJd/a3ic8tPZtwUmqNrxMRo54nfFaUvtt89q/+icQ+fd66c2xQHn0KyFkzJDoAUfpZA==", + "dev": true + }, + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", "dev": true, "requires": { - "acorn": "5.2.1", + "esrecurse": "4.2.0", + "estraverse": "4.2.0" + } + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha1-PzGA+y4pEBdxastMnW1bXDSmqB0=", + "dev": true + }, + "espree": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.2.tgz", + "integrity": "sha512-sadKeYwaR/aJ3stC2CdvgXu1T16TdYN+qwCpcWbMnGJ8s0zNWemzrvb2GbD4OhmJ/fwpJjudThAlLobGbWZbCQ==", + "dev": true, + "requires": { + "acorn": "5.3.0", "acorn-jsx": "3.0.1" } }, "esprima": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "integrity": "sha1-RJnt3NERDgshi6zy+n9/WfVcqAQ=", "dev": true }, "esquery": { @@ -553,26 +555,33 @@ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.35" - } - }, "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", "dev": true }, - "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "external-editor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.1.0.tgz", + "integrity": "sha1-PQJqIbf5W1cmOH1CAKwWDTcsO0g=", + "dev": true, + "requires": { + "chardet": "0.4.2", + "iconv-lite": "0.4.19", + "tmp": "0.0.33" + } + }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", "dev": true }, "fast-levenshtein": { @@ -582,13 +591,12 @@ "dev": true }, "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, "requires": { - "escape-string-regexp": "1.0.5", - "object-assign": "4.1.1" + "escape-string-regexp": "1.0.5" } }, "file-entry-cache": { @@ -619,21 +627,12 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "dev": true, - "requires": { - "is-property": "1.0.2" - } - }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -649,9 +648,9 @@ } }, "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.1.0.tgz", + "integrity": "sha512-uEuWt9mqTlPDwSqi+sHjD4nWU/1N+q0fiWI9T1mZpD2UENqX20CFD5T/ziLZvztPaBKl7ZylUi1q6Qfm7E2CiQ==", "dev": true }, "globby": { @@ -683,6 +682,12 @@ "ansi-regex": "2.1.1" } }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, "htmlparser2": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", @@ -722,6 +727,12 @@ } } }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha1-90aPYBNfXl2tM5nAqBvpoWA6CCs=", + "dev": true + }, "ignore": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", @@ -751,52 +762,32 @@ "dev": true }, "inquirer": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", - "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", "dev": true, "requires": { - "ansi-escapes": "1.4.0", - "ansi-regex": "2.1.1", - "chalk": "1.1.3", - "cli-cursor": "1.0.2", + "ansi-escapes": "3.0.0", + "chalk": "2.3.0", + "cli-cursor": "2.1.0", "cli-width": "2.2.0", - "figures": "1.7.0", + "external-editor": "2.1.0", + "figures": "2.0.0", "lodash": "4.17.4", - "readline2": "1.0.1", - "run-async": "0.1.0", - "rx-lite": "3.1.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", + "mute-stream": "0.0.7", + "run-async": "2.3.0", + "rx-lite": "4.0.8", + "rx-lite-aggregates": "4.0.8", + "string-width": "2.1.1", + "strip-ansi": "4.0.0", "through": "2.3.8" } }, - "interpret": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.4.tgz", - "integrity": "sha1-ggzdWIuGj/sZGoCVBtbJyPISsbA=", - "dev": true - }, "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-my-json-valid": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz", - "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", - "dev": true, - "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "jsonpointer": "4.0.1", - "xtend": "4.0.1" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true }, "is-path-cwd": { "version": "1.0.0", @@ -810,32 +801,29 @@ "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", "dev": true, "requires": { - "is-path-inside": "1.0.0" + "is-path-inside": "1.0.1" } }, "is-path-inside": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", - "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "dev": true, "requires": { "path-is-inside": "1.0.2" } }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, "is-resolvable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", - "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", - "dev": true, - "requires": { - "tryit": "1.0.3" - } + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.1.tgz", + "integrity": "sha512-y5CXYbzvB3jTnWAZH1Nl7ykUWb6T3BcTs56HUruwBf8MhF56n1HWqhDWnVFo8GHrUPDgvUUNVhrc2U8W7iqz5g==", + "dev": true }, "isarray": { "version": "1.0.0", @@ -843,6 +831,12 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -852,7 +846,7 @@ "js-yaml": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", - "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", + "integrity": "sha1-LnhEFka9RoLpY/IrbpKCPDCcYtw=", "dev": true, "requires": { "argparse": "1.0.9", @@ -889,25 +883,16 @@ } } }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", "dev": true }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, "levn": { @@ -926,6 +911,22 @@ "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", "dev": true }, + "lru-cache": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", + "integrity": "sha1-Yi4y6CSItJJ5EUpPns9F581rulU=", + "dev": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "mimic-fn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", + "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -965,9 +966,9 @@ "dev": true }, "mute-stream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", - "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, "natural-compare": { @@ -976,12 +977,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -998,10 +993,13 @@ } }, "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "1.1.0" + } }, "optimist": { "version": "0.6.1", @@ -1035,10 +1033,10 @@ } } }, - "os-homedir": { + "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, "parserlib": { @@ -1059,12 +1057,6 @@ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", "dev": true }, - "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", - "dev": true - }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -1087,9 +1079,9 @@ } }, "pluralize": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", - "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", "dev": true }, "prelude-ls": { @@ -1105,15 +1097,21 @@ "dev": true }, "progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", - "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, "readable-stream": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", "dev": true, "requires": { "core-util-is": "1.0.2", @@ -1125,37 +1123,6 @@ "util-deprecate": "1.0.2" } }, - "readline2": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", - "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "mute-stream": "0.0.5" - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "1.5.0" - }, - "dependencies": { - "resolve": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", - "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", - "dev": true, - "requires": { - "path-parse": "1.0.5" - } - } - } - }, "require-uncached": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", @@ -1179,62 +1146,90 @@ "dev": true }, "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true, "requires": { - "exit-hook": "1.1.1", - "onetime": "1.1.0" + "onetime": "2.0.1", + "signal-exit": "3.0.2" } }, "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", "dev": true, "requires": { "glob": "7.1.2" } }, "run-async": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", - "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", "dev": true, "requires": { - "once": "1.4.0" + "is-promise": "2.1.0" } }, "rx-lite": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", - "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", "dev": true }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "requires": { + "rx-lite": "4.0.8" + } + }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=", "dev": true }, - "shelljs": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", - "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha1-4FnAnYVx8FQII3M0M1BdOi8AsY4=", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "glob": "7.1.2", - "interpret": "1.0.4", - "rechoir": "0.6.2" + "shebang-regex": "1.0.0" } }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0" + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -1242,40 +1237,41 @@ "dev": true }, "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "string_decoder": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", "dev": true, "requires": { "safe-buffer": "5.1.1" } }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + } } }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, "strip-json-comments": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", @@ -1289,50 +1285,17 @@ "dev": true }, "table": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", - "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", "dev": true, "requires": { - "ajv": "4.11.8", - "ajv-keywords": "1.5.1", - "chalk": "1.1.3", + "ajv": "5.5.2", + "ajv-keywords": "2.1.1", + "chalk": "2.3.0", "lodash": "4.17.4", - "slice-ansi": "0.0.4", + "slice-ansi": "1.0.0", "string-width": "2.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - } } }, "text-table": { @@ -1347,11 +1310,14 @@ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, - "tryit": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", - "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", - "dev": true + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=", + "dev": true, + "requires": { + "os-tmpdir": "1.0.2" + } }, "type-check": { "version": "0.3.2", @@ -1368,21 +1334,21 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, - "user-home": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", - "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", - "dev": true, - "requires": { - "os-homedir": "1.0.2" - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha1-/wS9/AEO5UfXgL7DjhrBwnd9JTo=", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + }, "wordwrap": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", @@ -1404,10 +1370,10 @@ "mkdirp": "0.5.1" } }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true } } diff --git a/package.json b/package.json index f41b67b64..b0943cb2c 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "devDependencies": { "coffeelint": "^2.0.7", "csslint": "^1.0.5", - "eslint": "^3.17.1", + "eslint": "^4.14.0", + "eslint-config-google": "^0.9.1", "jshint": "^2.9.4" }, "repository": { From 4e0f7e9086fea82b96c1d2510998887637eb3acb Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 11:49:06 +1300 Subject: [PATCH 003/219] Use the old "var" syntax, and max line 120 --- .eslintrc.js | 3 --- .eslintrc.json | 11 +++++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) delete mode 100644 .eslintrc.js create mode 100644 .eslintrc.json diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index f2ddd9cea..000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - "extends": "google" -}; \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..a2035e465 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,11 @@ +{ + "extends": "google", + "env": { + "browser": true, + "node": true + }, + "rules": { + "no-var": "off", + "max-len": ["error", { "code": 120, "tabWidth": 4 }] + } +} \ No newline at end of file From a836d6432fc1e4eead9d6c3b2200c8405e495552 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 11:51:14 +1300 Subject: [PATCH 004/219] Updating javascript style --- spec/javascripts/graphs/bar_group_spec.js | 52 ++++----- .../graphs/bar_label_group_spec.js | 26 ++--- spec/javascripts/graphs/height_scale_spec.js | 16 ++- .../graphs/horizontal_bar_graph_spec.js | 110 +++++++++--------- spec/javascripts/graphs/width_scale_spec.js | 17 ++- 5 files changed, 104 insertions(+), 117 deletions(-) diff --git a/spec/javascripts/graphs/bar_group_spec.js b/spec/javascripts/graphs/bar_group_spec.js index 6358d1d26..50af8d3a8 100644 --- a/spec/javascripts/graphs/bar_group_spec.js +++ b/spec/javascripts/graphs/bar_group_spec.js @@ -1,4 +1,4 @@ -(function(){ +(function() { 'use strict'; /* @@ -6,31 +6,30 @@ */ describe('when drawing the group of bars', function() { - var BarGroup, subject, widthScale, bars, data; + var BarGroup; var subject; var widthScale; var bars; var data; - beforeEach(function() { + beforeEach(function() { + var WidthScale = growstuff.WidthScale; + BarGroup = growstuff.BarGroup; - var WidthScale = growstuff.WidthScale; - BarGroup = growstuff.BarGroup; + bars = [ + {name: 'Shade', value: 0.2}, + {name: 'Half Shade', value: 0.5}, + ]; - bars = [ - {name: 'Shade', value: 0.2}, - {name: 'Half Shade', value: 0.5} - ]; + data = { + bars: bars, + bar_color: 'steelblue', + width: {size: 300, scale: 'linear'}, + height: {size: 400, scale: 'ordinal'}, + }; - data = { - bars: bars, - bar_color: 'steelblue', - width: {size: 300, scale: 'linear'}, - height: {size: 400, scale: 'ordinal'} - }; + widthScale = new WidthScale(data); + subject = new BarGroup(data); + subject.render(d3.select('#jasmine_content').append('svg')); + }); - widthScale = new WidthScale(data); - subject = new BarGroup(data); - subject.render(d3.select('#jasmine_content').append('svg')); - }); - - it('draws a group', function(){ + it('draws a group', function() { expect($('g.bar')).toExist(); }); @@ -38,23 +37,20 @@ expect($('g.bar rect')).toHaveLength(2); }); - it('fills the bars with color', function(){ + it('fills the bars with color', function() { expect($('g.bar rect')).toHaveAttr('fill', 'steelblue'); }); - it('shows a tooltip on hover', function(){ + it('shows a tooltip on hover', function() { var i; - //get all of the title nodes for the bars + // get all of the title nodes for the bars var barNodes = $('g.bar rect title'); - for (i = 0; i < bars.length; i++){ + for (i = 0; i < bars.length; i++) { expect(barNodes[i].textContent) .toBe('This value is ' + bars[i].value + '' + '.'); } }); - - }); - }()); diff --git a/spec/javascripts/graphs/bar_label_group_spec.js b/spec/javascripts/graphs/bar_label_group_spec.js index aaaf680c7..7cb6e5537 100644 --- a/spec/javascripts/graphs/bar_label_group_spec.js +++ b/spec/javascripts/graphs/bar_label_group_spec.js @@ -1,42 +1,38 @@ -(function(){ +(function() { 'use strict'; /* This file contains tests for the labels that get rendered next to each bar */ - describe('BarLabelGroup', function(){ + describe('BarLabelGroup', function() { + var BarLabelGroup; var subject; var data; - var BarLabelGroup, subject, data; - - beforeEach(function(){ + beforeEach(function() { BarLabelGroup = growstuff.BarLabelGroup; var bars = [ {name: 'Shade', value: 0.2}, - {name: 'Half Shade', value: 0.5} + {name: 'Half Shade', value: 0.5}, ]; data = { - bars: bars + bars: bars, }; subject = new BarLabelGroup(data); subject.render(d3.select('#jasmine_content').append('svg')); }); - it('draws a group for labels', function(){ + it('draws a group for labels', function() { expect($('g.bar-label')).toExist(); }); - it('draws 2 bar labels', function(){ + it('draws 2 bar labels', function() { expect($('g.bar-label text')).toHaveLength(2); }); - it ('has text for 2 bar labels', function(){ - //jquery jasmine appends text from all text elements + it('has text for 2 bar labels', function() { + // jquery jasmine appends text from all text elements // into one string expect($('g.bar-label text')).toHaveText('ShadeHalf Shade'); - }); - }); - -}()); \ No newline at end of file +}()); diff --git a/spec/javascripts/graphs/height_scale_spec.js b/spec/javascripts/graphs/height_scale_spec.js index 0de6a987a..3a257400c 100644 --- a/spec/javascripts/graphs/height_scale_spec.js +++ b/spec/javascripts/graphs/height_scale_spec.js @@ -1,4 +1,4 @@ -(function(){ +(function() { 'use strict'; /* @@ -6,18 +6,18 @@ */ describe('HeightScale when specifying height', function() { - var data, bars, HeightScale, subject, mockD3; + var data; var bars; var HeightScale; var subject; var mockD3; - beforeEach(function(){ + beforeEach(function() { HeightScale = growstuff.HeightScale; bars = [ {name: 'Shade', value: 0.2}, - {name: 'Half Shade', value: 0.5} + {name: 'Half Shade', value: 0.5}, ]; data = { bars: bars, width: {size: 300, scale: 'linear'}, - height: {size: 400, scale: 'ordinal'} + height: {size: 400, scale: 'ordinal'}, }; subject = new HeightScale(data); @@ -26,12 +26,10 @@ mockD3.rangeRoundBands.and.returnValue(mockD3); spyOn(d3.scale, 'ordinal').and.returnValue(mockD3); subject.render(); - }); - it('calls the d3 range round bands function to draw the height', function(){ + it('calls the d3 range round bands function to draw the height', function() { expect(mockD3.rangeRoundBands).toHaveBeenCalledWith([0, 400], 0.05, 0); }); }); - -}()); \ No newline at end of file +}()); diff --git a/spec/javascripts/graphs/horizontal_bar_graph_spec.js b/spec/javascripts/graphs/horizontal_bar_graph_spec.js index 8e6b62a48..b8fbed1a3 100644 --- a/spec/javascripts/graphs/horizontal_bar_graph_spec.js +++ b/spec/javascripts/graphs/horizontal_bar_graph_spec.js @@ -1,4 +1,4 @@ -(function(){ +(function() { 'use strict'; /* @@ -6,70 +6,68 @@ are more integration-y type tests that require the full graph. */ - describe('HorizontalBarGraph', function() { - var BarLabelGroup, BarGroup, subject, data; + describe('HorizontalBarGraph', function() { + var BarLabelGroup; var BarGroup; var subject; var data; - beforeEach(function() { - var HorizontalBarGraph = growstuff.HorizontalBarGraph; - var bars = [ - {name: 'Shade', value: 0.2}, - {name: 'Half Shade', value: 0.5} - ]; - data = { - bars: bars, - bar_color: 'steelblue', - width: {size: 300, scale: 'linear'}, - height: {size: 400, scale: 'ordinal'}, - //left is used to shift the bars over so that there is - //room for the labels - margin: {top: 0, right: 0, bottom: 0, left: 100} - }; + beforeEach(function() { + var HorizontalBarGraph = growstuff.HorizontalBarGraph; + var bars = [ + {name: 'Shade', value: 0.2}, + {name: 'Half Shade', value: 0.5}, + ]; + data = { + bars: bars, + bar_color: 'steelblue', + width: {size: 300, scale: 'linear'}, + height: {size: 400, scale: 'ordinal'}, + // left is used to shift the bars over so that there is + // room for the labels + margin: {top: 0, right: 0, bottom: 0, left: 100}, + }; - subject = new HorizontalBarGraph(data); - BarGroup = growstuff.BarGroup; - BarLabelGroup = growstuff.BarLabelGroup; - expect(BarLabelGroup).toExist(); - spyOn(BarGroup.prototype, 'render').and.callThrough(); - spyOn(BarLabelGroup.prototype, 'render').and.callThrough(); - subject.render(d3.select($('#jasmine_content')[0])); - }); + subject = new HorizontalBarGraph(data); + BarGroup = growstuff.BarGroup; + BarLabelGroup = growstuff.BarLabelGroup; + expect(BarLabelGroup).toExist(); + spyOn(BarGroup.prototype, 'render').and.callThrough(); + spyOn(BarLabelGroup.prototype, 'render').and.callThrough(); + subject.render(d3.select($('#jasmine_content')[0])); + }); - it('draws a graph', function() { - expect($('#jasmine_content svg')).toExist(); - }); + it('draws a graph', function() { + expect($('#jasmine_content svg')).toExist(); + }); - it('draws a group for the whole graph', function(){ - expect($('g.bar-graph')).toExist(); - }); + it('draws a group for the whole graph', function() { + expect($('g.bar-graph')).toExist(); + }); - it('draws a bar group', function(){ - expect(BarGroup.prototype.render).toHaveBeenCalled(); - }); + it('draws a bar group', function() { + expect(BarGroup.prototype.render).toHaveBeenCalled(); + }); - it('draws a group of bar labels', function() { - expect(BarLabelGroup.prototype.render).toHaveBeenCalled(); - }); + it('draws a group of bar labels', function() { + expect(BarLabelGroup.prototype.render).toHaveBeenCalled(); + }); - it('has the expected width and height', function() { - var $svg = $('svg'); - var margin = data.margin; - expect($svg).toHaveAttr('width', (data.width.size + margin.left + margin.right) + ''); - expect($svg).toHaveAttr('height', (data.height.size + margin.top + margin.bottom) + ''); - }); + it('has the expected width and height', function() { + var $svg = $('svg'); + var margin = data.margin; + expect($svg).toHaveAttr('width', (data.width.size + margin.left + margin.right) + ''); + expect($svg).toHaveAttr('height', (data.height.size + margin.top + margin.bottom) + ''); + }); - it('draws the graph shifted to the right to accommodate for labels', function(){ - expect('g.bar-graph').toHaveAttr('transform', 'translate(100,0)'); - }); + it('draws the graph shifted to the right to accommodate for labels', function() { + expect('g.bar-graph').toHaveAttr('transform', 'translate(100,0)'); + }); - it ('on the x axis, draws at least one bar at max width less margin width because of domain and range mapping', function(){ - expect('g.bar rect:eq(1)').toHaveAttr('width', '300' ); - }); - - it ('on the y axis, all bars are the same height', function(){ - expect('g.bar rect:eq(0)').toHaveAttr('height', '195'); - expect('g.bar rect:eq(1)').toHaveAttr('height', '195'); - }); + it('on the x axis, draws at least one bar at max width less margin width because of domain and range mapping', function() { + expect('g.bar rect:eq(1)').toHaveAttr('width', '300' ); + }); + it('on the y axis, all bars are the same height', function() { + expect('g.bar rect:eq(0)').toHaveAttr('height', '195'); + expect('g.bar rect:eq(1)').toHaveAttr('height', '195'); + }); }); - }()); diff --git a/spec/javascripts/graphs/width_scale_spec.js b/spec/javascripts/graphs/width_scale_spec.js index c6ca9204a..d8e34234a 100644 --- a/spec/javascripts/graphs/width_scale_spec.js +++ b/spec/javascripts/graphs/width_scale_spec.js @@ -1,4 +1,4 @@ -(function(){ +(function() { 'use strict'; /* @@ -6,19 +6,19 @@ the length of a bar so that it is the correct size for the screen */ - describe('GraphScale, when specifying width', function(){ - var data, WidthScale, subject, mockD3; + describe('GraphScale, when specifying width', function() { + var data; var WidthScale; var subject; var mockD3; - beforeEach(function(){ + beforeEach(function() { WidthScale = growstuff.WidthScale; var bars = [ {name: 'Shade', value: 0.2}, - {name: 'Half Shade', value: 0.5} + {name: 'Half Shade', value: 0.5}, ]; data = { bars: bars, width: {size: 300, scale: 'linear'}, - height: {size: 400, scale: 'ordinal'} + height: {size: 400, scale: 'ordinal'}, }; subject = new WidthScale(data, 'width'); @@ -29,13 +29,12 @@ subject.render(); }); - it ('gets the value of the longest bar', function(){ + it('gets the value of the longest bar', function() { expect(subject.getMaxValue()).toEqual(0.5); }); - it ('calls the d3 range function to draw the width', function(){ + it('calls the d3 range function to draw the width', function() { expect(mockD3.range).toHaveBeenCalledWith([0, 300]); }); }); - }()); From 74646680212ffe0108b50e557542d453603b16eb Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 12:02:21 +1300 Subject: [PATCH 005/219] Add bootstrap js to codeclimate excludes --- .codeclimate.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.codeclimate.yml b/.codeclimate.yml index ca784c7a0..32904c026 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -42,3 +42,4 @@ exclude_paths: - spec/ - public/ - app/assets/stylesheets/bootstrap-accessibility.css +- app/assets/javascripts/bootstrap* From 5584da02c33ef1659dc6caf5e6c5aa1a609468b0 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 12:35:30 +1300 Subject: [PATCH 006/219] Some js cleanups --- app/assets/javascripts/application.js | 18 +++++----- app/assets/javascripts/graphs/bar_group.js | 33 +++++++++---------- .../javascripts/graphs/bar_label_group.js | 30 +++++++++-------- app/assets/javascripts/graphs/height_scale.js | 14 ++++---- .../graphs/horizontal_bar_graph.js | 24 ++++++++------ app/assets/javascripts/graphs/width_scale.js | 13 ++++---- app/assets/javascripts/posts.js | 16 ++++----- 7 files changed, 78 insertions(+), 70 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 0ad04350c..fc3369264 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -10,12 +10,12 @@ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD // GO AFTER THE REQUIRES BELOW. // -//= require leaflet -//= require leaflet.markercluster -//= require js-routes -//= require jquery -//= require jquery_ujs -//= require jquery-ui/autocomplete -//= require bootstrap-sprockets -//= require bootstrap-datepicker -//= require_tree . +// = require leaflet +// = require leaflet.markercluster +// = require js-routes +// = require jquery +// = require jquery_ujs +// = require jquery-ui/autocomplete +// = require bootstrap-sprockets +// = require bootstrap-datepicker +// = require_tree . diff --git a/app/assets/javascripts/graphs/bar_group.js b/app/assets/javascripts/graphs/bar_group.js index ceff6afdf..a6abaa150 100644 --- a/app/assets/javascripts/graphs/bar_group.js +++ b/app/assets/javascripts/graphs/bar_group.js @@ -1,7 +1,7 @@ -//= require graphs/width_scale -//= require graphs/height_scale +// = require graphs/width_scale +// = require graphs/height_scale -(function(){ +(function() { 'use strict'; /* @@ -17,34 +17,33 @@ function BarGroup(data) { this._data = data; } -BarGroup.prototype.render = function(root){ - +BarGroup.prototype.render = function(root) { var data = this._data; var bars = this._data.bars; var widthScale = new WidthScale(data).render(); var heightScale = new HeightScale(data).render(); return root.append('g') - .attr("class", "bar") - .selectAll("rect") - .data(bars.map(function(bar) { return bar.value; })) + .attr('class', 'bar') + .selectAll('rect') + .data(bars.map(function(bar) { + return bar.value; +})) .enter() - .append("rect") - .attr("y", function(d, i){ + .append('rect') + .attr('y', function(d, i) { return heightScale(i); - }) - .attr("height", heightScale.rangeBand()) - .attr("fill", data.bar_color) - .attr("width", function(d){ + .attr('height', heightScale.rangeBand()) + .attr('fill', data.bar_color) + .attr('width', function(d) { return widthScale(d); }) - .append("title") - .text(function(d){ + .append('title') + .text(function(d) { return 'This value is ' + d + '.'; }); }; growstuff.BarGroup = BarGroup; - }()); diff --git a/app/assets/javascripts/graphs/bar_label_group.js b/app/assets/javascripts/graphs/bar_label_group.js index bfe2eee51..e60073743 100644 --- a/app/assets/javascripts/graphs/bar_label_group.js +++ b/app/assets/javascripts/graphs/bar_label_group.js @@ -11,30 +11,32 @@ This file draws the labels to the left of each bar. this._data = data; } - BarLabelGroup.prototype.render = function(d3){ + BarLabelGroup.prototype.render = function(d3) { var bars = this._data.bars; - //vvcopy pasta from spike vv -- this is a good candidate for refactor + // vvcopy pasta from spike vv -- this is a good candidate for refactor var barHeight = 40; return d3.append('g') - .attr("class", "bar-label") - .selectAll("text") - .data(bars.map(function(bar){ return bar.name;})) + .attr('class', 'bar-label') + .selectAll('text') + .data(bars.map(function(bar) { + return bar.name; +})) .enter() - .append("text") + .append('text') .attr('x', -80) - .attr('y', function(d, i){ - //shrink the margin between each label to give them an even spread with - //bars + .attr('y', function(d, i) { + // shrink the margin between each label to give them an even spread with + // bars var barLabelSpread = 2/3; - //move them downward to line up with bars + // move them downward to line up with bars var barLabelTopEdge = 17; return i * barHeight * (barLabelSpread) + barLabelTopEdge; }) - .text(function(d){return d;}); - + .text(function(d) { +return d; +}); }; growstuff.BarLabelGroup = BarLabelGroup; - -}()); \ No newline at end of file +}()); diff --git a/app/assets/javascripts/graphs/height_scale.js b/app/assets/javascripts/graphs/height_scale.js index 7521d50f0..8abc636c9 100644 --- a/app/assets/javascripts/graphs/height_scale.js +++ b/app/assets/javascripts/graphs/height_scale.js @@ -1,20 +1,23 @@ -//=require d3 +// =require d3 /* Height Scale is used to map the number of bars to the display size of the svg */ -(function(){ +(function() { 'use strict'; var growstuff = (window.growstuff = window.growstuff || {}); - function HeightScale(data){ + /** + * new heighscale object + */ + function HeightScale(data) { this._data = data; } - HeightScale.prototype.render = function(){ + HeightScale.prototype.render = function() { var data = this._data; var scaleType = data.height.scale; var axisSize = data.height.size; @@ -25,5 +28,4 @@ the svg }; growstuff.HeightScale = HeightScale; - -}()); \ No newline at end of file +}()); diff --git a/app/assets/javascripts/graphs/horizontal_bar_graph.js b/app/assets/javascripts/graphs/horizontal_bar_graph.js index 9760138fb..856bca885 100644 --- a/app/assets/javascripts/graphs/horizontal_bar_graph.js +++ b/app/assets/javascripts/graphs/horizontal_bar_graph.js @@ -1,6 +1,6 @@ -//= require d3 -//= require graphs/bar_group -//= require graphs/bar_label_group +// = require d3 +// = require graphs/bar_group +// = require graphs/bar_label_group /* Horizontal Bar Graph represents sum total of the graph including all of the parts: @@ -17,6 +17,10 @@ The main dimensions of the graph are rendered here. var BarGroup = growstuff.BarGroup; var BarLabelGroup = growstuff.BarLabelGroup; + /** + * create a new graph object + * @param {??} bar graph data + */ function HorizontalBarGraph(data) { this._data = data; this._d3 = d3; @@ -33,12 +37,12 @@ The main dimensions of the graph are rendered here. var barGroup = new BarGroup(this._data); var svg = root - .append("svg") - .attr("width", width.size + margin.left + margin.right) - .attr("height", height.size + margin.top + margin.bottom) - .append("g") - .attr("class","bar-graph") - .attr("transform","translate(" + margin.left + "," + margin.top + ")"); + .append('svg') + .attr('width', width.size + margin.left + margin.right) + .attr('height', height.size + margin.top + margin.bottom) + .append('g') + .attr('class', 'bar-graph') + .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); barGroup.render(svg); @@ -48,4 +52,4 @@ The main dimensions of the graph are rendered here. }; growstuff.HorizontalBarGraph = HorizontalBarGraph; -}()); \ No newline at end of file +}()); diff --git a/app/assets/javascripts/graphs/width_scale.js b/app/assets/javascripts/graphs/width_scale.js index d8997c8c9..4a5c71e19 100644 --- a/app/assets/javascripts/graphs/width_scale.js +++ b/app/assets/javascripts/graphs/width_scale.js @@ -1,16 +1,16 @@ -//=require d3 +// =require d3 /* Width scale is used to map the value for the length of each bar to the display size of the svg */ -(function(){ +(function() { 'use strict'; var growstuff = (window.growstuff = window.growstuff || {}); - function WidthScale (data){ + function WidthScale(data) { this._data = data; } @@ -24,10 +24,11 @@ to the display size of the svg .range([0, axisSize]); }; - WidthScale.prototype.getMaxValue = function(){ - return d3.max(this._data.bars.map(function(bar) { return bar.value; })); + WidthScale.prototype.getMaxValue = function() { + return d3.max(this._data.bars.map(function(bar) { + return bar.value; +})); }; growstuff.WidthScale = WidthScale; - }()); diff --git a/app/assets/javascripts/posts.js b/app/assets/javascripts/posts.js index c2cf3ff4e..94fe0fb92 100644 --- a/app/assets/javascripts/posts.js +++ b/app/assets/javascripts/posts.js @@ -1,4 +1,4 @@ -$(document).ready(function () { +$(document).ready(function() { $('.post-like').show(); $('.post-like').on('ajax:success', function(event, data) { @@ -6,13 +6,13 @@ $(document).ready(function () { $('#post-' + data.id + ' .like-count').text(data.description); if (data.liked_by_member) { - like_control.data("method", "delete"); - like_control.attr("href", data.url); - like_control.text("Unlike"); + like_control.data('method', 'delete'); + like_control.attr('href', data.url); + like_control.text('Unlike'); } else { - like_control.data("method", "post"); - like_control.attr("href", '/likes.json?post_id=' + data.id); - like_control.text("Like"); + like_control.data('method', 'post'); + like_control.attr('href', '/likes.json?post_id=' + data.id); + like_control.text('Like'); } }); -}); \ No newline at end of file +}); From 0db2d6b3464d6ef42056c5939b3393fba59f116a Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 12:42:20 +1300 Subject: [PATCH 007/219] converted variable to camel case --- app/assets/javascripts/posts.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/posts.js b/app/assets/javascripts/posts.js index 94fe0fb92..37269e78e 100644 --- a/app/assets/javascripts/posts.js +++ b/app/assets/javascripts/posts.js @@ -2,17 +2,17 @@ $(document).ready(function() { $('.post-like').show(); $('.post-like').on('ajax:success', function(event, data) { - var like_control = $('#post-' + data.id + ' .post-like'); + var likeControl = $('#post-' + data.id + ' .post-like'); $('#post-' + data.id + ' .like-count').text(data.description); if (data.liked_by_member) { - like_control.data('method', 'delete'); - like_control.attr('href', data.url); - like_control.text('Unlike'); + likeControl.data('method', 'delete'); + likeControl.attr('href', data.url); + likeControl.text('Unlike'); } else { - like_control.data('method', 'post'); - like_control.attr('href', '/likes.json?post_id=' + data.id); - like_control.text('Like'); + likeControl.data('method', 'post'); + likeControl.attr('href', '/likes.json?post_id=' + data.id); + likeControl.text('Like'); } }); }); From 7c251615746cc3d417ce26c72c9739872c519210 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 12:48:13 +1300 Subject: [PATCH 008/219] contributors/commits update --- CONTRIBUTORS.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 69c9fd2e4..f984400ae 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -8,14 +8,15 @@ submit the change with your pull request. ## Committers -- Alex Bayley / [Skud](https://github.com/Skud) - Cesy / [cesy](https://github.com/cesy) - Miles Gould / [pozorvlak](https://github.com/pozorvlak) -- Taylor Griffin / [tygriffin](https://github.com/tygriffin) - Mackenzie Morgan / [maco](https://github.com/maco) +- Brenda Wallace / [br3nda](https://github.com/br3nda) ## Contributors +- Alex Bayley / [Skud](https://github.com/Skud) +- Taylor Griffin / [tygriffin](https://github.com/tygriffin) - Joseph Caudle / [jcaudle](https://github.com/jcaudle) - Ricky Amianym / [amianym](https://github.com/amianym) - Juliet Kemp / [julietk](https://github.com/julietk) @@ -73,7 +74,6 @@ submit the change with your pull request. - Lucas Nogueira / [lucasnogueira](https://github.com/lucasnogueira) - Charley Lewittes / [ctlewitt](https://github.com/ctlewitt) - Kristine Nicole Polvoriza / [polveenomials](https://github.com/polveenomials) -- Brenda Wallace / [br3nda](https://github.com/br3nda) - Jim Stallings / [jestallin](https://github.com/jestallin) - Alyssa Ransbury / [alran](https://github.com/alran) - Thomas Countz / [thomascountz](https://github.com/thomascountz) From e910bfd50fa76aec98139cb2e5effd7a5eb5cb4b Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 12:54:33 +1300 Subject: [PATCH 009/219] added JSDoc --- app/assets/javascripts/graphs/bar_group.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/javascripts/graphs/bar_group.js b/app/assets/javascripts/graphs/bar_group.js index a6abaa150..312fcd6f3 100644 --- a/app/assets/javascripts/graphs/bar_group.js +++ b/app/assets/javascripts/graphs/bar_group.js @@ -13,6 +13,10 @@ var WidthScale = growstuff.WidthScale; var HeightScale = growstuff.HeightScale; +/** + * data object for bar group + * @param {int} data The graph data + */ function BarGroup(data) { this._data = data; } From 591c95dca2a6f49d27d8fd2ebea04b0486f2d839 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 12:56:52 +1300 Subject: [PATCH 010/219] added JSDoc --- app/assets/javascripts/graphs/width_scale.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/javascripts/graphs/width_scale.js b/app/assets/javascripts/graphs/width_scale.js index 4a5c71e19..891cfaf96 100644 --- a/app/assets/javascripts/graphs/width_scale.js +++ b/app/assets/javascripts/graphs/width_scale.js @@ -10,6 +10,10 @@ to the display size of the svg var growstuff = (window.growstuff = window.growstuff || {}); + /** + * Object for WidthScale + * @param {?} data Graph data + */ function WidthScale(data) { this._data = data; } From 122a7f17e211173c8ab162e5db93c80d2e37d2eb Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 13:00:09 +1300 Subject: [PATCH 011/219] Fix up JSDoc --- app/assets/javascripts/graphs/bar_group.js | 2 +- app/assets/javascripts/graphs/height_scale.js | 1 + app/assets/javascripts/graphs/horizontal_bar_graph.js | 2 +- app/assets/javascripts/graphs/width_scale.js | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/graphs/bar_group.js b/app/assets/javascripts/graphs/bar_group.js index 312fcd6f3..cb6bf0ef6 100644 --- a/app/assets/javascripts/graphs/bar_group.js +++ b/app/assets/javascripts/graphs/bar_group.js @@ -15,7 +15,7 @@ /** * data object for bar group - * @param {int} data The graph data + * @param {Object} graph configuration */ function BarGroup(data) { this._data = data; diff --git a/app/assets/javascripts/graphs/height_scale.js b/app/assets/javascripts/graphs/height_scale.js index 8abc636c9..c4034a349 100644 --- a/app/assets/javascripts/graphs/height_scale.js +++ b/app/assets/javascripts/graphs/height_scale.js @@ -12,6 +12,7 @@ the svg /** * new heighscale object + * @param {Object} data Graph configuration */ function HeightScale(data) { this._data = data; diff --git a/app/assets/javascripts/graphs/horizontal_bar_graph.js b/app/assets/javascripts/graphs/horizontal_bar_graph.js index 856bca885..12dcae588 100644 --- a/app/assets/javascripts/graphs/horizontal_bar_graph.js +++ b/app/assets/javascripts/graphs/horizontal_bar_graph.js @@ -19,7 +19,7 @@ The main dimensions of the graph are rendered here. /** * create a new graph object - * @param {??} bar graph data + * @param {Object} data Graph configuration */ function HorizontalBarGraph(data) { this._data = data; diff --git a/app/assets/javascripts/graphs/width_scale.js b/app/assets/javascripts/graphs/width_scale.js index 891cfaf96..3ddd0decf 100644 --- a/app/assets/javascripts/graphs/width_scale.js +++ b/app/assets/javascripts/graphs/width_scale.js @@ -12,7 +12,7 @@ to the display size of the svg /** * Object for WidthScale - * @param {?} data Graph data + * @param {Object} data Graph configuration */ function WidthScale(data) { this._data = data; From 4570fda3846788500733442185bc5accddc145be Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 13:03:10 +1300 Subject: [PATCH 012/219] removed 2 unused variable in js --- app/assets/javascripts/graphs/height_scale.js | 1 - app/assets/javascripts/graphs/horizontal_bar_graph.js | 1 - 2 files changed, 2 deletions(-) diff --git a/app/assets/javascripts/graphs/height_scale.js b/app/assets/javascripts/graphs/height_scale.js index c4034a349..6a4f29528 100644 --- a/app/assets/javascripts/graphs/height_scale.js +++ b/app/assets/javascripts/graphs/height_scale.js @@ -21,7 +21,6 @@ the svg HeightScale.prototype.render = function() { var data = this._data; var scaleType = data.height.scale; - var axisSize = data.height.size; return d3.scale[scaleType]() .domain(d3.range(data.bars.length)) diff --git a/app/assets/javascripts/graphs/horizontal_bar_graph.js b/app/assets/javascripts/graphs/horizontal_bar_graph.js index 12dcae588..ea3853e94 100644 --- a/app/assets/javascripts/graphs/horizontal_bar_graph.js +++ b/app/assets/javascripts/graphs/horizontal_bar_graph.js @@ -27,7 +27,6 @@ The main dimensions of the graph are rendered here. } HorizontalBarGraph.prototype.render = function(root) { - var bars = this._data.bars; var width = this._data.width; var height = this._data.height; From 978317f47a13937539d2c80aba6e8d2dcc239e5f Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 14:20:01 +1300 Subject: [PATCH 013/219] more JSDoc --- app/assets/javascripts/graphs/bar_group.js | 2 +- app/assets/javascripts/graphs/bar_label_group.js | 4 ++++ app/assets/javascripts/graphs/height_scale.js | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/graphs/bar_group.js b/app/assets/javascripts/graphs/bar_group.js index cb6bf0ef6..a0d18050d 100644 --- a/app/assets/javascripts/graphs/bar_group.js +++ b/app/assets/javascripts/graphs/bar_group.js @@ -15,7 +15,7 @@ /** * data object for bar group - * @param {Object} graph configuration + * @param {Object} data Graph configuration */ function BarGroup(data) { this._data = data; diff --git a/app/assets/javascripts/graphs/bar_label_group.js b/app/assets/javascripts/graphs/bar_label_group.js index e60073743..3c574d6ad 100644 --- a/app/assets/javascripts/graphs/bar_label_group.js +++ b/app/assets/javascripts/graphs/bar_label_group.js @@ -7,6 +7,10 @@ This file draws the labels to the left of each bar. var growstuff = (window.growstuff = window.growstuff || {}); + /** + * new bar label object + * @param {Object} data Graph configuration + */ function BarLabelGroup(data) { this._data = data; } diff --git a/app/assets/javascripts/graphs/height_scale.js b/app/assets/javascripts/graphs/height_scale.js index 6a4f29528..f4b76a044 100644 --- a/app/assets/javascripts/graphs/height_scale.js +++ b/app/assets/javascripts/graphs/height_scale.js @@ -11,7 +11,7 @@ the svg var growstuff = (window.growstuff = window.growstuff || {}); /** - * new heighscale object + * new height scale object * @param {Object} data Graph configuration */ function HeightScale(data) { From 2d98a7e793a4a12ca3012a4a80847cdbaa315063 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 14:21:29 +1300 Subject: [PATCH 014/219] Wrapped a long line --- spec/javascripts/graphs/horizontal_bar_graph_spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/javascripts/graphs/horizontal_bar_graph_spec.js b/spec/javascripts/graphs/horizontal_bar_graph_spec.js index b8fbed1a3..16f35f018 100644 --- a/spec/javascripts/graphs/horizontal_bar_graph_spec.js +++ b/spec/javascripts/graphs/horizontal_bar_graph_spec.js @@ -61,7 +61,8 @@ expect('g.bar-graph').toHaveAttr('transform', 'translate(100,0)'); }); - it('on the x axis, draws at least one bar at max width less margin width because of domain and range mapping', function() { + it('on the x axis, draws at least one bar at max width less margin width', function() { + // because of domain and range mapping expect('g.bar rect:eq(1)').toHaveAttr('width', '300' ); }); From 56b056278542c9979f91dcf48b831a416624f540 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 14:24:08 +1300 Subject: [PATCH 015/219] Removed unused variables in js --- spec/javascripts/graphs/bar_group_spec.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spec/javascripts/graphs/bar_group_spec.js b/spec/javascripts/graphs/bar_group_spec.js index 50af8d3a8..a76992a6c 100644 --- a/spec/javascripts/graphs/bar_group_spec.js +++ b/spec/javascripts/graphs/bar_group_spec.js @@ -6,10 +6,9 @@ */ describe('when drawing the group of bars', function() { - var BarGroup; var subject; var widthScale; var bars; var data; + var BarGroup; var subject; var bars; var data; beforeEach(function() { - var WidthScale = growstuff.WidthScale; BarGroup = growstuff.BarGroup; bars = [ @@ -24,7 +23,6 @@ height: {size: 400, scale: 'ordinal'}, }; - widthScale = new WidthScale(data); subject = new BarGroup(data); subject.render(d3.select('#jasmine_content').append('svg')); }); From 0a71bfe99f6fdb6f1fea3d1a7f63d6ad1d9497eb Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 14:40:22 +1300 Subject: [PATCH 016/219] Tabulation and jsdoc fixes --- app/assets/javascripts/graphs/bar_group.js | 36 +++++++++---------- .../javascripts/graphs/bar_label_group.js | 17 +++++---- app/assets/javascripts/graphs/height_scale.js | 6 ++-- .../graphs/horizontal_bar_graph.js | 12 +++---- app/assets/javascripts/graphs/width_scale.js | 7 ++-- 5 files changed, 38 insertions(+), 40 deletions(-) diff --git a/app/assets/javascripts/graphs/bar_group.js b/app/assets/javascripts/graphs/bar_group.js index a0d18050d..e8a90c20a 100644 --- a/app/assets/javascripts/graphs/bar_group.js +++ b/app/assets/javascripts/graphs/bar_group.js @@ -1,13 +1,14 @@ +// =require d3 // = require graphs/width_scale // = require graphs/height_scale +/* + * This represents bars for a bar graph. + * Currently these are used for HorizontalBarGraph. + */ (function() { 'use strict'; - /* - This represents bars for a bar graph. - Currently these are used for HorizontalBarGraph. - */ var growstuff = (window.growstuff = window.growstuff || {}); var WidthScale = growstuff.WidthScale; @@ -20,19 +21,18 @@ function BarGroup(data) { this._data = data; } + BarGroup.prototype.render = function(root) { + var data = this._data; + var bars = this._data.bars; + var widthScale = new WidthScale(data).render(); + var heightScale = new HeightScale(data).render(); -BarGroup.prototype.render = function(root) { - var data = this._data; - var bars = this._data.bars; - var widthScale = new WidthScale(data).render(); - var heightScale = new HeightScale(data).render(); - - return root.append('g') - .attr('class', 'bar') - .selectAll('rect') - .data(bars.map(function(bar) { - return bar.value; -})) + return root.append('g') + .attr('class', 'bar') + .selectAll('rect') + .data(bars.map(function(bar) { + return bar.value; + })) .enter() .append('rect') .attr('y', function(d, i) { @@ -47,7 +47,7 @@ BarGroup.prototype.render = function(root) { .text(function(d) { return 'This value is ' + d + '.'; }); -}; + }; -growstuff.BarGroup = BarGroup; + growstuff.BarGroup = BarGroup; }()); diff --git a/app/assets/javascripts/graphs/bar_label_group.js b/app/assets/javascripts/graphs/bar_label_group.js index 3c574d6ad..2da31f567 100644 --- a/app/assets/javascripts/graphs/bar_label_group.js +++ b/app/assets/javascripts/graphs/bar_label_group.js @@ -1,12 +1,11 @@ +// =require d3 +/** + * This file draws the labels to the left of each bar. + */ (function() { 'use strict'; -/* -This file draws the labels to the left of each bar. - */ - var growstuff = (window.growstuff = window.growstuff || {}); - /** * new bar label object * @param {Object} data Graph configuration @@ -24,8 +23,8 @@ This file draws the labels to the left of each bar. .attr('class', 'bar-label') .selectAll('text') .data(bars.map(function(bar) { - return bar.name; -})) + return bar.name; + })) .enter() .append('text') .attr('x', -80) @@ -38,8 +37,8 @@ This file draws the labels to the left of each bar. return i * barHeight * (barLabelSpread) + barLabelTopEdge; }) .text(function(d) { -return d; -}); + return d; + }); }; growstuff.BarLabelGroup = BarLabelGroup; diff --git a/app/assets/javascripts/graphs/height_scale.js b/app/assets/javascripts/graphs/height_scale.js index f4b76a044..5189ef800 100644 --- a/app/assets/javascripts/graphs/height_scale.js +++ b/app/assets/javascripts/graphs/height_scale.js @@ -1,8 +1,8 @@ // =require d3 -/* -Height Scale is used to map the number of bars to the display size of -the svg +/** + * Height Scale is used to map the number of bars to the display size of + * the svg */ (function() { diff --git a/app/assets/javascripts/graphs/horizontal_bar_graph.js b/app/assets/javascripts/graphs/horizontal_bar_graph.js index ea3853e94..ef2333ef1 100644 --- a/app/assets/javascripts/graphs/horizontal_bar_graph.js +++ b/app/assets/javascripts/graphs/horizontal_bar_graph.js @@ -2,12 +2,12 @@ // = require graphs/bar_group // = require graphs/bar_label_group -/* -Horizontal Bar Graph represents sum total of the graph including all of the parts: -Bars -Bar Labels - -The main dimensions of the graph are rendered here. +/** + * Horizontal Bar Graph represents sum total of the graph including all of the parts: + * Bars + * Bar Labels + * + * The main dimensions of the graph are rendered here. */ (function() { diff --git a/app/assets/javascripts/graphs/width_scale.js b/app/assets/javascripts/graphs/width_scale.js index 3ddd0decf..4ba275eda 100644 --- a/app/assets/javascripts/graphs/width_scale.js +++ b/app/assets/javascripts/graphs/width_scale.js @@ -1,10 +1,9 @@ // =require d3 -/* -Width scale is used to map the value for the length of each bar -to the display size of the svg +/** + * Width scale is used to map the value for the length of each bar + * to the display size of the svg */ - (function() { 'use strict'; From ee1617c7c3cdf0573fca117d97080be9c9b1fea9 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 14:43:29 +1300 Subject: [PATCH 017/219] Added new line at end of file --- spec/javascripts/support/spec_helper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/javascripts/support/spec_helper.js b/spec/javascripts/support/spec_helper.js index 465207cf3..f9d26ae25 100644 --- a/spec/javascripts/support/spec_helper.js +++ b/spec/javascripts/support/spec_helper.js @@ -3,4 +3,4 @@ beforeEach(function() { $(document).off(); $('body').off(); $('#jasmine_content').replaceWith($('
')); -}); \ No newline at end of file +}); From a39c304fac6d06ea4701da360ab1256b11cda884 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 15:06:55 +1300 Subject: [PATCH 018/219] Fix ordering of crops' parent on form --- app/views/crops/_form.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/crops/_form.html.haml b/app/views/crops/_form.html.haml index c391c79d2..a8cfe0bea 100644 --- a/app/views/crops/_form.html.haml +++ b/app/views/crops/_form.html.haml @@ -51,7 +51,7 @@ .form-group = f.label :parent_id, 'Parent crop', class: 'control-label col-md-2' .col-md-8 - = collection_select(:crop, :parent_id, Crop.all, :id, :name, { include_blank: true }, class: 'form-control') + = collection_select(:crop, :parent_id, Crop.all.order(:name), :id, :name, { include_blank: true }, class: 'form-control') %span.help-block Optional. For setting up crop hierarchies for varieties etc. From d86de897c74aded2180c312badc8657c7317ae0d Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 15:12:09 +1300 Subject: [PATCH 019/219] Wrap long line --- app/views/crops/_form.html.haml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/crops/_form.html.haml b/app/views/crops/_form.html.haml index a8cfe0bea..c16041f97 100644 --- a/app/views/crops/_form.html.haml +++ b/app/views/crops/_form.html.haml @@ -51,7 +51,8 @@ .form-group = f.label :parent_id, 'Parent crop', class: 'control-label col-md-2' .col-md-8 - = collection_select(:crop, :parent_id, Crop.all.order(:name), :id, :name, { include_blank: true }, class: 'form-control') + = collection_select(:crop, :parent_id, Crop.all.order(:name), :id, :name, + { include_blank: true }, class: 'form-control') %span.help-block Optional. For setting up crop hierarchies for varieties etc. From 26f525e88acb28dc6ca9132b437bdfdf5d3d47c6 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 17:06:54 +1300 Subject: [PATCH 020/219] Upgrade gems for rails 5 Note: Unlocks elastic search gem, and removes debuggers --- Gemfile | 24 +++-- Gemfile.lock | 260 ++++++++++++++++++++++----------------------------- 2 files changed, 123 insertions(+), 161 deletions(-) diff --git a/Gemfile b/Gemfile index c5d5d1fe1..a5c7b7dce 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ source 'https://rubygems.org' ruby '2.4.1' -gem 'rails', '~> 4.2.8' +gem 'rails', '5.1.4' gem 'bundler', '>=1.1.5' @@ -22,7 +22,7 @@ gem 'font-awesome-sass' gem 'uglifier' # JavaScript compressor # planting and harvest predictions -gem 'active_median' +# gem 'active_median' gem 'flickraw' gem 'jquery-rails' @@ -88,7 +88,7 @@ gem 'd3-rails', '~> 3.5' # 4.* produces Error: : could not find an objec # Project does not use semver, so we want to be in sync with the version of # elasticsearch we use # See https://github.com/elastic/elasticsearch-ruby#compatibility -gem "elasticsearch-api", "~> 2.0.0" +gem "elasticsearch-api" gem "elasticsearch-model" gem "elasticsearch-rails" gem "hashie", ">= 3.5.3" @@ -99,7 +99,7 @@ gem 'rake', '>= 10.0.0' gem "responders" # allows soft delete. Used for members. -gem 'acts_as_paranoid', '~> 0.5.0' +gem "paranoia", "~> 2.2" gem 'xmlrpc' # fixes rake error - can be removed if not needed later @@ -113,16 +113,14 @@ group :production, :staging do end group :development do - # A debugger and irb alternative. Pry doesn't play nice - # with unicorn, so start a Webrick server when debugging - # with Pry - gem 'better_errors', '~> 2.2.0' - gem 'binding_of_caller' - gem 'guard' - gem 'guard-rspec' + gem 'better_errors' gem 'letter_opener' - gem 'pry' - gem 'quiet_assets' + gem 'listen' + # gem 'binding_of_caller' + # gem 'guard' + # gem 'guard-rspec' + # gem 'pry' + # gem 'quiet_assets' end group :development, :test do diff --git a/Gemfile.lock b/Gemfile.lock index 31113ea38..bc54de685 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,71 +2,67 @@ GEM remote: https://rubygems.org/ remote: https://rails-assets.org/ specs: - actionmailer (4.2.10) - actionpack (= 4.2.10) - actionview (= 4.2.10) - activejob (= 4.2.10) + actioncable (5.1.4) + actionpack (= 5.1.4) + nio4r (~> 2.0) + websocket-driver (~> 0.6.1) + actionmailer (5.1.4) + actionpack (= 5.1.4) + actionview (= 5.1.4) + activejob (= 5.1.4) mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 1.0, >= 1.0.5) - actionpack (4.2.10) - actionview (= 4.2.10) - activesupport (= 4.2.10) - rack (~> 1.6) - rack-test (~> 0.6.2) - rails-dom-testing (~> 1.0, >= 1.0.5) + rails-dom-testing (~> 2.0) + actionpack (5.1.4) + actionview (= 5.1.4) + activesupport (= 5.1.4) + rack (~> 2.0) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (4.2.10) - activesupport (= 4.2.10) + actionview (5.1.4) + activesupport (= 5.1.4) builder (~> 3.1) - erubis (~> 2.7.0) - rails-dom-testing (~> 1.0, >= 1.0.5) + erubi (~> 1.4) + rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) active_link_to (1.0.5) actionpack addressable - active_median (0.1.4) - activerecord active_merchant-paypal-bogus-gateway (0.1.0) activemerchant active_utils (3.3.9) activesupport (>= 3.2, < 5.2.0) i18n - activejob (4.2.10) - activesupport (= 4.2.10) - globalid (>= 0.3.0) + activejob (5.1.4) + activesupport (= 5.1.4) + globalid (>= 0.3.6) activemerchant (1.75.0) activesupport (>= 3.2.14, < 6.x) builder (>= 2.1.2, < 4.0.0) i18n (>= 0.6.9) nokogiri (~> 1.4) - activemodel (4.2.10) - activesupport (= 4.2.10) - builder (~> 3.1) - activerecord (4.2.10) - activemodel (= 4.2.10) - activesupport (= 4.2.10) - arel (~> 6.0) - activesupport (4.2.10) + activemodel (5.1.4) + activesupport (= 5.1.4) + activerecord (5.1.4) + activemodel (= 5.1.4) + activesupport (= 5.1.4) + arel (~> 8.0) + activesupport (5.1.4) + concurrent-ruby (~> 1.0, >= 1.0.2) i18n (~> 0.7) minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - acts_as_paranoid (0.5.0) - activerecord (>= 4.0, < 5.1) - activesupport (>= 4.0, < 5.1) addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) - arel (6.0.4) + arel (8.0.0) ast (2.3.0) - autoprefixer-rails (7.2.3) + autoprefixer-rails (7.2.4) execjs bcrypt (3.1.11) - better_errors (2.2.0) + better_errors (2.4.0) coderay (>= 1.0.0) - erubis (>= 2.6.6) + erubi (>= 1.0.0) rack (>= 0.9.0) - binding_of_caller (0.7.3) - debug_inspector (>= 0.0.1) bluecloth (2.2.0) bonsai-elasticsearch-rails (0.2.0) elasticsearch-model (~> 0) @@ -79,20 +75,19 @@ GEM bootstrap-sass (3.3.7) autoprefixer-rails (>= 5.2.1) sass (>= 3.3.4) - bootstrap_form (2.7.0) builder (3.2.3) bullet (5.7.0) activesupport (>= 3.0.0) uniform_notifier (~> 1.10.0) byebug (9.1.0) cancancan (2.1.2) - capybara (2.16.1) + capybara (2.17.0) addressable mini_mime (>= 0.1.3) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) - xpath (~> 2.0) + xpath (>= 2.0, < 4.0) capybara-email (2.5.0) capybara (~> 2.4) mail @@ -105,10 +100,8 @@ GEM cliver (0.3.2) cocaine (0.5.8) climate_control (>= 0.0.3, < 1.0) - codeclimate-test-reporter (1.0.8) - simplecov (<= 0.13) - codemirror-rails (5.16.0) - railties (>= 3.0, < 6.0) + codeclimate-test-reporter (1.0.7) + simplecov coderay (1.1.2) coffee-rails (4.2.2) coffee-script (>= 2.2.0) @@ -117,29 +110,25 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - comfortable_mexican_sofa (1.12.10) + comfortable_mexican_sofa (1.8.2) active_link_to (>= 1.0.0) - bootstrap-sass (>= 3.2.0) - bootstrap_form (>= 2.2.0) - codemirror-rails (>= 3.0.0) coffee-rails (>= 3.1.0) + formatted_form (>= 2.1.0) haml-rails (>= 0.3.0) jquery-rails (>= 3.0.0) - jquery-ui-rails (>= 5.0.0) - kramdown (>= 1.0.0) - paperclip (>= 4.0.0) - plupload-rails (>= 1.2.1) - rails (>= 4.0.0, < 5.1) - rails-i18n (>= 4.0.0) - sass-rails (>= 4.0.3) + jquery-ui-rails (>= 4.0.0) + paperclip (>= 3.4.0) + rails (>= 3.1.0) + redcarpet (>= 2.2.0) + sass-rails (>= 3.1.0) concurrent-ruby (1.0.5) connection_pool (2.2.1) - coveralls (0.8.19) - json (>= 1.8, < 3) - simplecov (~> 0.12.0) - term-ansicolor (~> 1.3) - thor (~> 0.19.1) - tins (~> 1.6) + coveralls (0.7.1) + multi_json (~> 1.3) + rest-client + simplecov (>= 0.7) + term-ansicolor + thor crass (1.0.3) csv_shaper (1.3.0) activesupport (>= 3.0.0) @@ -147,8 +136,7 @@ GEM railties (>= 3.1) dalli (2.7.6) database_cleaner (1.6.2) - debug_inspector (0.0.3) - devise (4.3.0) + devise (4.4.0) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0, < 5.2) @@ -156,23 +144,26 @@ GEM warden (~> 1.2.3) diff-lcs (1.3) docile (1.1.5) + domain_name (0.5.20170404) + unf (>= 0.0.5, < 1.0.0) easy_translate (0.5.0) json thread thread_safe - elasticsearch (2.0.2) - elasticsearch-api (= 2.0.2) - elasticsearch-transport (= 2.0.2) - elasticsearch-api (2.0.2) + elasticsearch (6.0.0) + elasticsearch-api (= 6.0.0) + elasticsearch-transport (= 6.0.0) + elasticsearch-api (6.0.0) multi_json elasticsearch-model (0.1.9) activesupport (> 3) elasticsearch (> 0.4) hashie elasticsearch-rails (0.1.9) - elasticsearch-transport (2.0.2) + elasticsearch-transport (6.0.0) faraday multi_json + erubi (1.7.0) erubis (2.7.0) excon (0.60.0) execjs (2.7.0) @@ -191,7 +182,8 @@ GEM flickraw (0.9.9) font-awesome-sass (4.7.0) sass (>= 3.2) - formatador (0.2.5) + formatted_form (2.1.2) + rails (>= 3.1) friendly_id (5.2.3) activerecord (>= 4.0.0) geocoder (1.4.5) @@ -203,20 +195,6 @@ GEM gravatar-ultimate (2.0.0) activesupport (>= 2.3.14) rack - guard (2.14.1) - formatador (>= 0.2.4) - listen (>= 2.7, < 4.0) - lumberjack (~> 1.0) - nenv (~> 0.1) - notiffany (~> 0.0) - pry (>= 0.9.12) - shellany (~> 0.0) - thor (>= 0.18.1) - guard-compat (1.2.1) - guard-rspec (4.7.3) - guard (~> 2.1) - guard-compat (~> 1.1) - rspec (>= 2.99.0, < 4.0) haml (5.0.4) temple (>= 0.8.0) tilt @@ -250,6 +228,8 @@ GEM haml (>= 4.0, < 6) nokogiri (>= 1.6.0) ruby_parser (~> 3.5) + http-cookie (1.0.3) + domain_name (~> 0.5) httparty (0.15.6) multi_xml (>= 0.5.2) i18n (0.9.1) @@ -298,12 +278,11 @@ GEM kaminari-core (= 1.1.1) kaminari-core (1.1.1) kgio (2.11.1) - kramdown (1.16.2) launchy (2.4.3) addressable (~> 2.3) leaflet-rails (1.2.0) rails (>= 4.2.0) - letter_opener (1.4.1) + letter_opener (1.5.0) launchy (~> 2.2) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) @@ -312,7 +291,6 @@ GEM loofah (2.1.1) crass (~> 1.0.2) nokogiri (>= 1.5.9) - lumberjack (1.0.12) mail (2.7.0) mini_mime (>= 0.1.1) memcachier (0.0.2) @@ -323,18 +301,16 @@ GEM mimemagic (0.3.2) mini_mime (1.0.0) mini_portile2 (2.3.0) - minitest (5.10.3) + minitest (5.11.1) moneta (0.8.1) multi_json (1.11.3) multi_xml (0.6.0) multipart-post (2.0.0) - nenv (0.3.0) + netrc (0.11.0) newrelic_rpm (4.7.1.340) + nio4r (2.2.0) nokogiri (1.8.1) mini_portile2 (~> 2.3.0) - notiffany (0.1.1) - nenv (~> 0.1) - shellany (~> 0.0) oauth (0.5.4) oauth2 (1.4.0) faraday (>= 0.8, < 0.13) @@ -342,7 +318,7 @@ GEM multi_json (~> 1.3) multi_xml (~> 0.5) rack (>= 1.2, < 3) - omniauth (1.7.1) + omniauth (1.8.1) hashie (>= 3.4.6, < 3.6.0) rack (>= 1.6.2, < 3) omniauth-facebook (4.0.0) @@ -367,6 +343,8 @@ GEM mime-types mimemagic (~> 0.3.0) parallel (1.12.1) + paranoia (2.4.0) + activerecord (>= 4.0, < 5.2) parser (2.4.0.2) ast (~> 2.3) pg (0.21.0) @@ -374,57 +352,46 @@ GEM platform-api (2.1.0) heroics (~> 0.0.23) moneta (~> 0.8.1) - plupload-rails (1.2.1) - rails (>= 3.1) poltergeist (1.17.0) capybara (~> 2.1) cliver (~> 0.3.1) websocket-driver (>= 0.2.0) powerpack (0.1.1) - pry (0.11.3) - coderay (~> 1.1.0) - method_source (~> 0.9.0) public_suffix (3.0.1) - quiet_assets (1.1.0) - railties (>= 3.1, < 5.0) - rack (1.6.8) + rack (2.0.3) rack-protection (2.0.0) rack - rack-test (0.6.3) - rack (>= 1.0) - rails (4.2.10) - actionmailer (= 4.2.10) - actionpack (= 4.2.10) - actionview (= 4.2.10) - activejob (= 4.2.10) - activemodel (= 4.2.10) - activerecord (= 4.2.10) - activesupport (= 4.2.10) - bundler (>= 1.3.0, < 2.0) - railties (= 4.2.10) - sprockets-rails + rack-test (0.8.2) + rack (>= 1.0, < 3) + rails (5.1.4) + actioncable (= 5.1.4) + actionmailer (= 5.1.4) + actionpack (= 5.1.4) + actionview (= 5.1.4) + activejob (= 5.1.4) + activemodel (= 5.1.4) + activerecord (= 5.1.4) + activesupport (= 5.1.4) + bundler (>= 1.3.0) + railties (= 5.1.4) + sprockets-rails (>= 2.0.0) rails-assets-leaflet (1.2.0) rails-assets-leaflet.markercluster (1.2.0) rails-assets-leaflet (>= 1.0.3) - rails-deprecated_sanitizer (1.0.3) - activesupport (>= 4.2.0.alpha) - rails-dom-testing (1.0.9) - activesupport (>= 4.2.0, < 5.0) - nokogiri (~> 1.6) - rails-deprecated_sanitizer (>= 1.0.1) + rails-dom-testing (2.0.3) + activesupport (>= 4.2.0) + nokogiri (>= 1.6) rails-html-sanitizer (1.0.3) loofah (~> 2.0) - rails-i18n (4.0.9) - i18n (~> 0.7) - railties (~> 4.0) rails_12factor (0.0.3) rails_serve_static_assets rails_stdout_logging rails_serve_static_assets (0.0.5) rails_stdout_logging (0.0.5) - railties (4.2.10) - actionpack (= 4.2.10) - activesupport (= 4.2.10) + railties (5.1.4) + actionpack (= 5.1.4) + activesupport (= 5.1.4) + method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.1.0) @@ -433,14 +400,15 @@ GEM rb-fsevent (0.10.2) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) + redcarpet (3.4.0) redis (4.0.1) responders (2.4.0) actionpack (>= 4.2.0, < 5.3) railties (>= 4.2.0, < 5.3) - rspec (3.7.0) - rspec-core (~> 3.7.0) - rspec-expectations (~> 3.7.0) - rspec-mocks (~> 3.7.0) + rest-client (2.0.2) + http-cookie (>= 1.0.2, < 2.0) + mime-types (>= 1.16, < 4.0) + netrc (~> 0.8) rspec-activemodel-mocks (1.0.3) activemodel (>= 3.0) activesupport (>= 3.0) @@ -490,13 +458,12 @@ GEM childprocess (~> 0.5) rubyzip (~> 1.0) sexp_processor (4.10.0) - shellany (0.0.1) sidekiq (5.0.5) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) rack-protection (>= 1.5.0) redis (>= 3.3.4, < 5) - simplecov (0.12.0) + simplecov (0.15.1) docile (~> 1.1.0) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) @@ -516,7 +483,7 @@ GEM tins (~> 1.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - thor (0.19.4) + thor (0.20.0) thread (0.2.2) thread_safe (0.3.6) tilt (2.0.8) @@ -525,8 +492,11 @@ GEM trollop (1.16.2) tzinfo (1.2.4) thread_safe (~> 0.1) - uglifier (4.0.2) + uglifier (4.1.2) execjs (>= 0.3.0, < 3) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.4) unicode-display_width (1.3.0) unicorn (5.4.0) kgio (~> 2.6) @@ -538,25 +508,22 @@ GEM nokogiri (>= 1.2.0) rack (>= 1.0) rack-test (>= 0.5.3) - websocket-driver (0.7.0) + websocket-driver (0.6.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.3) will_paginate (3.1.6) xmlrpc (0.3.0) - xpath (2.1.0) - nokogiri (~> 1.3) + xpath (3.0.0) + nokogiri (~> 1.8) PLATFORMS ruby DEPENDENCIES - active_median active_merchant-paypal-bogus-gateway active_utils activemerchant - acts_as_paranoid (~> 0.5.0) - better_errors (~> 2.2.0) - binding_of_caller + better_errors bluecloth bonsai-elasticsearch-rails bootstrap-datepicker-rails @@ -578,7 +545,7 @@ DEPENDENCIES dalli database_cleaner devise - elasticsearch-api (~> 2.0.0) + elasticsearch-api elasticsearch-model elasticsearch-rails factory_bot_rails @@ -590,8 +557,6 @@ DEPENDENCIES geocoder gibbon (~> 1.2.0) gravatar-ultimate - guard - guard-rspec haml haml-i18n-extractor haml-rails @@ -606,18 +571,18 @@ DEPENDENCIES kaminari leaflet-rails letter_opener + listen memcachier newrelic_rpm omniauth (~> 1.3) omniauth-facebook omniauth-flickr (>= 0.0.15) omniauth-twitter + paranoia (~> 2.2) pg platform-api poltergeist - pry - quiet_assets - rails (~> 4.2.8) + rails (= 5.1.4) rails-assets-leaflet.markercluster! rails_12factor rainbow (< 2.2.0) @@ -638,7 +603,6 @@ DEPENDENCIES will_paginate xmlrpc - RUBY VERSION ruby 2.4.1p111 From f1c8743623873d449a54702858904fa3ee253447 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 17:10:50 +1300 Subject: [PATCH 021/219] Config for rails 5 --- config/application.rb | 16 +++---- config/environments/development.rb | 61 +++++++++++++----------- config/environments/production.rb | 74 ++++++++++++++++++------------ config/environments/staging.rb | 2 +- config/environments/test.rb | 43 ++++++++--------- 5 files changed, 109 insertions(+), 87 deletions(-) diff --git a/config/application.rb b/config/application.rb index 5280dce7e..3a07ca429 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,17 +1,17 @@ -require File.expand_path('../boot', __FILE__) +require_relative 'boot' require 'rails/all' require 'openssl' -if defined?(Bundler) - # If you precompile assets before deploying to production, use this line - Bundler.require(*Rails.groups(assets: %w(development test))) - # If you want your assets lazily compiled in production, use this line - # Bundler.require(:default, :assets, Rails.env) -end +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(*Rails.groups) module Growstuff class Application < Rails::Application + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults 5.1 + # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. @@ -105,6 +105,6 @@ module Growstuff # didn't work for us. config.cloudmade_key = '29a2d9e3cb3d429490a8f338b2388b1d' - config.active_record.raise_in_transactional_callbacks = true + # config.active_record.raise_in_transactional_callbacks = true end end diff --git a/config/environments/development.rb b/config/environments/development.rb index d3242353e..39abd19f1 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,48 +1,57 @@ -Growstuff::Application.configure do - # Settings specified here will take precedence over those in config/application.rb - - # Do not eager load code on boot. - config.eager_load = false +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. # In the development environment your application's code is reloaded on # every request. This slows down response time but is perfect for development # since you don't have to restart the web server when you make code changes. config.cache_classes = false - # Show full error reports and disable caching - config.consider_all_requests_local = true + # Do not eager load code on boot. + config.eager_load = false - # cache for testing/experimentation - turn off for normal dev use - config.action_controller.perform_caching = false - config.cache_store = :memory_store + # Show full error reports. + config.consider_all_requests_local = true - # Don't care if the mailer can't send + # Enable/disable caching. By default caching is disabled. + if Rails.root.join('tmp', 'caching-dev.txt').exist? + config.action_controller.perform_caching = true + + config.cache_store = :memory_store + config.public_file_server.headers = { + 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}" + } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false - # Print deprecation notices to the Rails logger + config.action_mailer.perform_caching = false + + # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log - # Only use best-standards-support built into browsers - config.action_dispatch.best_standards_support = :builtin + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = :page_load - # Do not compress assets - config.assets.compress = false - - # Expands the lines which load the assets + # Debug mode disables concatenation and preprocessing of assets. + # This option may cause significant delays in view rendering with a large + # number of complex assets. config.assets.debug = true - # Asset digests allow you to set far-future HTTP expiration dates on all assets, - # yet still be able to expire them through the digest params. - config.assets.digest = true - - # Adds additional error checking when serving assets at runtime. - # Checks for improperly declared sprockets dependencies. - # Raises helpful error messages. - config.assets.raise_runtime_errors = true + # Suppress logger output for asset requests. + config.assets.quiet = true # Raises error for missing translations # config.action_view.raise_on_missing_translations = true + # Use an evented file watcher to asynchronously detect changes in source code, + # routes, locales, etc. This feature depends on the listen gem. + config.file_watcher = ActiveSupport::EventedFileUpdateChecker + # Growstuff config config.action_mailer.default_url_options = { host: 'localhost:3000' } diff --git a/config/environments/production.rb b/config/environments/production.rb index c931c058e..99e25d577 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,5 +1,8 @@ -Growstuff::Application.configure do - # Settings specified here will take precedence over those in config/application.rb +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.cache_classes = true # Eager load code on boot. This eager loads most of Rails and # your application in memory, allowing both threaded web servers @@ -7,57 +10,67 @@ Growstuff::Application.configure do # Rake tasks automatically ignore this option for performance. config.eager_load = true - # Code is not reloaded between requests - config.cache_classes = true - - # Full error reports are disabled and caching is turned on + # Full error reports are disabled and caching is turned on. config.consider_all_requests_local = false config.action_controller.perform_caching = true - # Disable Rails's static asset server (Apache or nginx will already do this) - config.serve_static_files = false + # Attempt to read encrypted secrets from `config/secrets.yml.enc`. + # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or + # `config/secrets.yml.key`. + config.read_encrypted_secrets = true + + # Disable serving static files from the `/public` folder by default since + # Apache or NGINX already handles this. + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? # Compress JavaScripts and CSS. config.assets.js_compressor = :uglifier - config.assets.css_compressor = :sass + # config.assets.css_compressor = :sass - # Don't fallback to assets pipeline if a precompiled asset is missed - config.assets.compile = true + # Do not fallback to assets pipeline if a precompiled asset is missed. + config.assets.compile = false - # Generate digests for assets URLs - config.assets.digest = true + # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb - # Specifies the header that your server uses for sending files - # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache - # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.action_controller.asset_host = 'http://assets.example.com' + + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + + # Mount Action Cable outside main process or domain + # config.action_cable.mount_path = nil + # config.action_cable.url = 'wss://example.com/cable' + # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. # config.force_ssl = true - # See everything in the log (default is :info) - # config.log_level = :debug + # Use the lowest log level to ensure availability of diagnostic information + # when problems arise. + config.log_level = :debug - # Prepend all log lines with the following tags - # config.log_tags = [ :subdomain, :uuid ] + # Prepend all log lines with the following tags. + config.log_tags = [:request_id] - # Use a different logger for distributed setups - # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) + # Use a different cache store in production. + # config.cache_store = :mem_cache_store - # Use a different cache store in production - config.cache_store = :dalli_store - - # Enable serving of images, stylesheets, and JavaScripts from an asset server - # config.action_controller.asset_host = "http://assets.example.com" + # Use a real queuing backend for Active Job (and separate queues per environment) + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "growstuff_#{Rails.env}" + config.action_mailer.perform_caching = false # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. # config.action_mailer.raise_delivery_errors = false # Enable locale fallbacks for I18n (makes lookups for any locale fall back to - # the I18n.default_locale when a translation can not be found) + # the I18n.default_locale when a translation cannot be found). config.i18n.fallbacks = true - # Send deprecation notices to registered listeners + # Send deprecation notices to registered listeners. config.active_support.deprecation = :notify # Use default logging formatter so that PID and timestamp are not suppressed. @@ -103,4 +116,7 @@ Growstuff::Application.configure do end config.active_job.queue_adapter = :sidekiq + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false end diff --git a/config/environments/staging.rb b/config/environments/staging.rb index a9e242381..ce1eeaf7c 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -1,4 +1,4 @@ -Growstuff::Application.configure do +Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb config.action_controller.action_on_unpermitted_parameters = :raise diff --git a/config/environments/test.rb b/config/environments/test.rb index bf4135f64..43a86a23a 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,43 +1,40 @@ -Growstuff::Application.configure do - # Settings specified here will take precedence over those in config/application.rb +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # The test environment is used exclusively to run your application's + # test suite. You never need to work with it otherwise. Remember that + # your test database is "scratch space" for the test suite and is wiped + # and recreated between test runs. Don't rely on the data there! + config.cache_classes = true # Do not eager load code on boot. This avoids loading your whole application # just for the purpose of running a single test. If you are using a tool that # preloads Rails for running tests, you may have to set it to true. config.eager_load = false - # Allow lazy compilation of assets. Required for running Jasmine tests via - # `rake spec:javascript`. - config.assets.compile = true + # Configure public file server for tests with Cache-Control for performance. + config.public_file_server.enabled = true + config.public_file_server.headers = { + 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}" + } - # The test environment is used exclusively to run your application's - # test suite. You never need to work with it otherwise. Remember that - # your test database is "scratch space" for the test suite and is wiped - # and recreated between test runs. Don't rely on the data there! - - # Reload model classes when changed: otherwise Spork tests old versions. - config.cache_classes = false - - # Configure static asset server for tests with Cache-Control for performance - config.serve_static_files = true - config.static_cache_control = "public, max-age=3600" - - # Show full error reports and disable caching + # Show full error reports and disable caching. config.consider_all_requests_local = true config.action_controller.perform_caching = false - # Raise exceptions instead of rendering exception templates - config.action_dispatch.show_exceptions = true + # Raise exceptions instead of rendering exception templates. + config.action_dispatch.show_exceptions = false - # Disable request forgery protection in test environment + # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false + config.action_mailer.perform_caching = false # Tell Action Mailer not to deliver emails to the real world. # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test - # Print deprecation notices to the stderr + # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr # Raises error for missing translations @@ -46,7 +43,7 @@ Growstuff::Application.configure do # Growstuff config config.action_mailer.default_url_options = { host: 'localhost:8080' } - Growstuff::Application.configure do + Rails.application.configure do config.host = 'test.example.com' config.analytics_code = '' config.currency = 'AUD' From f4af682d454e5f1b024a9f6f083b86a32ebefa74 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 17:12:17 +1300 Subject: [PATCH 022/219] Updated code for Rail 5 --- Rakefile | 2 +- app/assets/javascripts/crops.js.erb | 4 +- app/assets/javascripts/members.js.erb | 4 +- app/assets/javascripts/places.js.erb | 6 +- app/controllers/orders_controller.rb | 4 +- app/helpers/application_helper.rb | 4 +- app/models/account.rb | 2 +- app/models/member.rb | 4 +- app/views/layouts/application.html.haml | 2 +- bin/rails | 2 +- bin/setup | 38 ++ bin/update | 29 + bin/yarn | 11 + config.ru | 2 +- config/boot.rb | 7 +- config/cable.yml | 10 + config/environment.rb | 8 +- .../application_controller_renderer.rb | 8 + config/initializers/assets.rb | 14 + config/initializers/cookies_serializer.rb | 5 + .../initializers/filter_parameter_logging.rb | 4 + config/initializers/geocoder.rb | 4 +- config/initializers/inflections.rb | 11 +- config/initializers/mime_types.rb | 1 - .../new_framework_defaults_5_1.rb | 14 + config/initializers/session_store.rb | 4 +- config/initializers/wrap_parameters.rb | 10 +- config/puma.rb | 56 ++ config/routes.rb | 6 +- config/spring.rb | 6 + db/schema.rb | 629 +++++++++--------- db/seeds.rb | 2 +- lib/haml/filters/growstuff_markdown.rb | 2 +- spec/helpers/application_helper_spec.rb | 2 +- .../haml/filters/growstuff_markdown_spec.rb | 2 +- spec/models/member_spec.rb | 2 +- spec/views/layouts/application_spec.rb | 2 +- spec/views/orders/show.html.haml_spec.rb | 2 +- spec/views/shop/index_spec.rb | 8 +- 39 files changed, 551 insertions(+), 382 deletions(-) create mode 100755 bin/setup create mode 100755 bin/update create mode 100755 bin/yarn create mode 100644 config/cable.yml create mode 100644 config/initializers/application_controller_renderer.rb create mode 100644 config/initializers/assets.rb create mode 100644 config/initializers/cookies_serializer.rb create mode 100644 config/initializers/filter_parameter_logging.rb create mode 100644 config/initializers/new_framework_defaults_5_1.rb create mode 100644 config/puma.rb create mode 100644 config/spring.rb diff --git a/Rakefile b/Rakefile index 7e5e8eed3..22553c357 100755 --- a/Rakefile +++ b/Rakefile @@ -5,4 +5,4 @@ require 'rake/dsl_definition' require File.expand_path('../config/application', __FILE__) -Growstuff::Application.load_tasks +Rails.application.load_tasks diff --git a/app/assets/javascripts/crops.js.erb b/app/assets/javascripts/crops.js.erb index afa16a009..f2eec8848 100644 --- a/app/assets/javascripts/crops.js.erb +++ b/app/assets/javascripts/crops.js.erb @@ -1,8 +1,8 @@ //= require graphs/horizontal_bar_graph if (document.getElementById("cropmap") !== null) { - mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>"; - mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>"; + mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Rails.application.config.mapbox_map_id %>"; + mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Rails.application.config.mapbox_access_token %>"; mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token; L.Icon.Default.imagePath = '/assets' diff --git a/app/assets/javascripts/members.js.erb b/app/assets/javascripts/members.js.erb index 500e79b25..1a3158f52 100644 --- a/app/assets/javascripts/members.js.erb +++ b/app/assets/javascripts/members.js.erb @@ -1,6 +1,6 @@ if (document.getElementById("membermap") !== null) { - mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>"; - mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>"; + mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Rails.application.config.mapbox_map_id %>"; + mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Rails.application.config.mapbox_access_token %>"; mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token; L.Icon.Default.imagePath = '/assets' diff --git a/app/assets/javascripts/places.js.erb b/app/assets/javascripts/places.js.erb index 48cb960d7..ad8b76266 100644 --- a/app/assets/javascripts/places.js.erb +++ b/app/assets/javascripts/places.js.erb @@ -1,10 +1,10 @@ if (document.getElementById("placesmap") !== null) { places_base_path = "/places"; - mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>"; - mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>"; + mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Rails.application.config.mapbox_map_id %>"; + mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Rails.application.config.mapbox_access_token %>"; mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token; nominatim_base_url = 'http://nominatim.openstreetmap.org/search/'; - nominatim_user_agent_email = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.user_agent_email %>"; + nominatim_user_agent_email = "<%= Rails.env == 'test' ? 0 : Rails.application.config.user_agent_email %>"; L.Icon.Default.imagePath = '/assets' diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb index 3bc9fe45c..31c709148 100644 --- a/app/controllers/orders_controller.rb +++ b/app/controllers/orders_controller.rb @@ -34,7 +34,7 @@ class OrdersController < ApplicationController response = EXPRESS_GATEWAY.setup_purchase( @order.total, items: @order.activemerchant_items, - currency: Growstuff::Application.config.currency, + currency: Rails.application.config.currency, no_shipping: true, ip: request.remote_ip, return_url: complete_order_url, @@ -51,7 +51,7 @@ class OrdersController < ApplicationController if params[:token] && params['PayerID'] purchase = EXPRESS_GATEWAY.purchase( @order.total, - currency: Growstuff::Application.config.currency, + currency: Rails.application.config.currency, ip: request.remote_ip, payer_id: params['PayerID'], token: params[:token] diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 639d27686..4e010cef1 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -5,7 +5,7 @@ module ApplicationHelper # 999 cents becomes 9.99 AUD -- for products/orders/etc def price_with_currency(price) - format('%.2f %s', price / 100.0, Growstuff::Application.config.currency) + format('%.2f %s', price / 100.0, Rails.application.config.currency) end def parse_date(str) @@ -15,7 +15,7 @@ module ApplicationHelper def forex_link(price) pid = price_in_dollars(price) - currency = Growstuff::Application.config.currency + currency = Rails.application.config.currency link = "http://www.wolframalpha.com/input/?i=#{pid}+#{currency}" link_to "(convert)", link, target: "_blank", rel: "noopener noreferrer" diff --git a/app/models/account.rb b/app/models/account.rb index 646e9db78..be14f406a 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -9,7 +9,7 @@ class Account < ActiveRecord::Base before_create do |account| unless account.account_type account.account_type = AccountType.find_or_create_by(name: - Growstuff::Application.config.default_account_type) + Rails.application.config.default_account_type) end end diff --git a/app/models/member.rb b/app/models/member.rb index 9828a005c..80a5ba840 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -210,7 +210,7 @@ class Member < ActiveRecord::Base def newsletter_subscribe(gb = Gibbon::API.new, testing = false) return true if Rails.env.test? && !testing gb.lists.subscribe( - id: Growstuff::Application.config.newsletter_list_id, + id: Rails.application.config.newsletter_list_id, email: { email: email }, merge_vars: { login_name: login_name }, double_optin: false # they already confirmed their email with us @@ -219,7 +219,7 @@ class Member < ActiveRecord::Base def newsletter_unsubscribe(gb = Gibbon::API.new, testing = false) return true if Rails.env.test? && !testing - gb.lists.unsubscribe(id: Growstuff::Application.config.newsletter_list_id, + gb.lists.unsubscribe(id: Rails.application.config.newsletter_list_id, email: { email: email }) end diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 7fe945d8a..d7e05a282 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -28,4 +28,4 @@ / Placed at the end of the document so the pages load faster = javascript_include_tag "application" - != Growstuff::Application.config.analytics_code + != Rails.application.config.analytics_code diff --git a/bin/rails b/bin/rails index 5191e6927..073966023 100755 --- a/bin/rails +++ b/bin/rails @@ -1,4 +1,4 @@ #!/usr/bin/env ruby -APP_PATH = File.expand_path('../../config/application', __FILE__) +APP_PATH = File.expand_path('../config/application', __dir__) require_relative '../config/boot' require 'rails/commands' diff --git a/bin/setup b/bin/setup new file mode 100755 index 000000000..78c4e861d --- /dev/null +++ b/bin/setup @@ -0,0 +1,38 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a starting point to setup your application. + # Add necessary setup steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + # Install JavaScript dependencies if using Yarn + # system('bin/yarn') + + + # puts "\n== Copying sample files ==" + # unless File.exist?('config/database.yml') + # cp 'config/database.yml.sample', 'config/database.yml' + # end + + puts "\n== Preparing database ==" + system! 'bin/rails db:setup' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end diff --git a/bin/update b/bin/update new file mode 100755 index 000000000..a8e4462f2 --- /dev/null +++ b/bin/update @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a way to update your development environment automatically. + # Add necessary update steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + puts "\n== Updating database ==" + system! 'bin/rails db:migrate' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end diff --git a/bin/yarn b/bin/yarn new file mode 100755 index 000000000..c2bacef83 --- /dev/null +++ b/bin/yarn @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby +VENDOR_PATH = File.expand_path('..', __dir__) +Dir.chdir(VENDOR_PATH) do + begin + exec "yarnpkg #{ARGV.join(" ")}" + rescue Errno::ENOENT + $stderr.puts "Yarn executable was not detected in the system." + $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" + exit 1 + end +end diff --git a/config.ru b/config.ru index d30ee4f18..bd83b2541 100644 --- a/config.ru +++ b/config.ru @@ -1,4 +1,4 @@ # This file is used by Rack-based servers to start the application. require ::File.expand_path('../config/environment', __FILE__) -run Growstuff::Application +run Rails.application diff --git a/config/boot.rb b/config/boot.rb index f2830ae31..30f5120df 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,6 +1,3 @@ -require 'rubygems' +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) -# Set up gems listed in the Gemfile. -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) - -require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) +require 'bundler/setup' # Set up gems listed in the Gemfile. diff --git a/config/cable.yml b/config/cable.yml new file mode 100644 index 000000000..b6110a25b --- /dev/null +++ b/config/cable.yml @@ -0,0 +1,10 @@ +development: + adapter: async + +test: + adapter: async + +production: + adapter: redis + url: redis://localhost:6379/1 + channel_prefix: growstuff_production diff --git a/config/environment.rb b/config/environment.rb index b175f6a30..426333bb4 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,5 +1,5 @@ -# Load the rails application -require File.expand_path('../application', __FILE__) +# Load the Rails application. +require_relative 'application' -# Initialize the rails application -Growstuff::Application.initialize! +# Initialize the Rails application. +Rails.application.initialize! diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb new file mode 100644 index 000000000..89d2efab2 --- /dev/null +++ b/config/initializers/application_controller_renderer.rb @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +# ActiveSupport::Reloader.to_prepare do +# ApplicationController.renderer.defaults.merge!( +# http_host: 'example.org', +# https: false +# ) +# end diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb new file mode 100644 index 000000000..4b828e80c --- /dev/null +++ b/config/initializers/assets.rb @@ -0,0 +1,14 @@ +# Be sure to restart your server when you modify this file. + +# Version of your assets, change this if you want to expire all your assets. +Rails.application.config.assets.version = '1.0' + +# Add additional assets to the asset load path. +# Rails.application.config.assets.paths << Emoji.images_path +# Add Yarn node_modules folder to the asset load path. +Rails.application.config.assets.paths << Rails.root.join('node_modules') + +# Precompile additional assets. +# application.js, application.css, and all non-JS/CSS in the app/assets +# folder are already added. +# Rails.application.config.assets.precompile += %w( admin.js admin.css ) diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb new file mode 100644 index 000000000..1389e86a3 --- /dev/null +++ b/config/initializers/cookies_serializer.rb @@ -0,0 +1,5 @@ +# Be sure to restart your server when you modify this file. + +# Specify a serializer for the signed and encrypted cookie jars. +# Valid options are :json, :marshal, and :hybrid. +Rails.application.config.action_dispatch.cookies_serializer = :marshal diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb new file mode 100644 index 000000000..4a994e1e7 --- /dev/null +++ b/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Configure sensitive parameters which will be filtered from the log file. +Rails.application.config.filter_parameters += [:password] diff --git a/config/initializers/geocoder.rb b/config/initializers/geocoder.rb index afe41f574..b31f1edf3 100644 --- a/config/initializers/geocoder.rb +++ b/config/initializers/geocoder.rb @@ -5,8 +5,8 @@ Geocoder.configure( timeout: 10, http_headers: { "User-Agent" => - "#{Growstuff::Application.config.user_agent} #{Growstuff::Application.config.user_agent_email}", - "From" => Growstuff::Application.config.user_agent_email + "#{Rails.application.config.user_agent} #{Rails.application.config.user_agent_email}", + "From" => Rails.application.config.user_agent_email } ) # This configuration takes precedence over environment/test.rb diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index 599d85bf4..22df44c07 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -1,16 +1,17 @@ # Be sure to restart your server when you modify this file. -# Add new inflection rules using the following format -# (all these examples are active by default): -# ActiveSupport::Inflector.inflections do |inflect| +# Add new inflection rules using the following format. Inflections +# are locale specific, and you may define rules for as many different +# locales as you wish. All of these examples are active by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| # inflect.plural /^(ox)$/i, '\1en' # inflect.singular /^(ox)en/i, '\1' # inflect.irregular 'person', 'people' # inflect.uncountable %w( fish sheep ) # end -# + # These inflection rules are supported but not enabled by default: -# ActiveSupport::Inflector.inflections do |inflect| +# ActiveSupport::Inflector.inflections(:en) do |inflect| # inflect.acronym 'RESTful' # end diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index 72aca7e44..dc1899682 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -2,4 +2,3 @@ # Add new mime types for use in respond_to blocks: # Mime::Type.register "text/richtext", :rtf -# Mime::Type.register_alias "text/html", :iphone diff --git a/config/initializers/new_framework_defaults_5_1.rb b/config/initializers/new_framework_defaults_5_1.rb new file mode 100644 index 000000000..9010abd5c --- /dev/null +++ b/config/initializers/new_framework_defaults_5_1.rb @@ -0,0 +1,14 @@ +# Be sure to restart your server when you modify this file. +# +# This file contains migration options to ease your Rails 5.1 upgrade. +# +# Once upgraded flip defaults one by one to migrate to the new default. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. + +# Make `form_with` generate non-remote forms. +Rails.application.config.action_view.form_with_generates_remote_forms = false + +# Unknown asset fallback will return the path passed in when the given +# asset is not present in the asset pipeline. +# Rails.application.config.assets.unknown_asset_fallback = false diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index 25119c9bc..9fc739015 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -1,8 +1,8 @@ # Be sure to restart your server when you modify this file. -Growstuff::Application.config.session_store :cookie_store, key: '_growstuff_session' +Rails.application.config.session_store :cookie_store, key: '_growstuff_session' # Use the database for sessions instead of the cookie-based default, # which shouldn't be used to store highly confidential information # (create the session table with "rails generate session_migration") -# Growstuff::Application.config.session_store :active_record_store +# Rails.application.config.session_store :active_record_store diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb index 999df2018..bbfc3961b 100644 --- a/config/initializers/wrap_parameters.rb +++ b/config/initializers/wrap_parameters.rb @@ -1,5 +1,5 @@ # Be sure to restart your server when you modify this file. -# + # This file contains settings for ActionController::ParamsWrapper which # is enabled by default. @@ -8,7 +8,7 @@ ActiveSupport.on_load(:action_controller) do wrap_parameters format: [:json] end -# Disable root element in JSON by default. -ActiveSupport.on_load(:active_record) do - self.include_root_in_json = false -end +# To enable root element in JSON for ActiveRecord objects. +# ActiveSupport.on_load(:active_record) do +# self.include_root_in_json = true +# end diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 000000000..1e19380dc --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,56 @@ +# Puma can serve each request in a thread from an internal thread pool. +# The `threads` method setting takes two numbers: a minimum and maximum. +# Any libraries that use thread pools should be configured to match +# the maximum value specified for Puma. Default is set to 5 threads for minimum +# and maximum; this matches the default thread size of Active Record. +# +threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } +threads threads_count, threads_count + +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +# +port ENV.fetch("PORT") { 3000 } + +# Specifies the `environment` that Puma will run in. +# +environment ENV.fetch("RAILS_ENV") { "development" } + +# Specifies the number of `workers` to boot in clustered mode. +# Workers are forked webserver processes. If using threads and workers together +# the concurrency of the application would be max `threads` * `workers`. +# Workers do not work on JRuby or Windows (both of which do not support +# processes). +# +# workers ENV.fetch("WEB_CONCURRENCY") { 2 } + +# Use the `preload_app!` method when specifying a `workers` number. +# This directive tells Puma to first boot the application and load code +# before forking the application. This takes advantage of Copy On Write +# process behavior so workers use less memory. If you use this option +# you need to make sure to reconnect any threads in the `on_worker_boot` +# block. +# +# preload_app! + +# If you are preloading your application and using Active Record, it's +# recommended that you close any connections to the database before workers +# are forked to prevent connection leakage. +# +# before_fork do +# ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) +# end + +# The code in the `on_worker_boot` will be called if you are using +# clustered mode by specifying a number of `workers`. After each worker +# process is booted, this block will be run. If you are using the `preload_app!` +# option, you will want to use this block to reconnect to any threads +# or connections that may have been created at application boot, as Ruby +# cannot share connections between processes. +# +# on_worker_boot do +# ActiveRecord::Base.establish_connection if defined?(ActiveRecord) +# end +# + +# Allow puma to be restarted by `rails restart` command. +plugin :tmp_restart diff --git a/config/routes.rb b/config/routes.rb index eb963311a..013fee209 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,4 @@ -Growstuff::Application.routes.draw do +Rails.application.routes.draw do get '/robots.txt' => 'robots#robots' resources :plant_parts @@ -89,7 +89,7 @@ Growstuff::Application.routes.draw do get '/shop' => 'shop#index' get '/shop/:action' => 'shop#:action' - comfy_route :cms_admin, path: '/admin/cms' + # comfy_route :cms_admin, path: '/admin/cms' namespace :admin do resources :members end @@ -113,5 +113,5 @@ Growstuff::Application.routes.draw do get '/.well-known/acme-challenge/:id' => 'pages#letsencrypt' # CMS stuff -- must remain LAST - comfy_route :cms, path: '/', sitemap: false + # comfy_route :cms, path: '/', sitemap: false end diff --git a/config/spring.rb b/config/spring.rb new file mode 100644 index 000000000..c9119b40c --- /dev/null +++ b/config/spring.rb @@ -0,0 +1,6 @@ +%w( + .ruby-version + .rbenv-vars + tmp/restart.txt + tmp/caching-dev.txt +).each { |path| Spring.watch(path) } diff --git a/db/schema.rb b/db/schema.rb index 473e756fa..ea66548f1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,4 +1,3 @@ -# encoding: UTF-8 # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. @@ -11,371 +10,352 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20171129041341) do +ActiveRecord::Schema.define(version: 20180103024400) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" - create_table "account_types", force: :cascade do |t| - t.string "name", null: false - t.boolean "is_paid" - t.boolean "is_permanent_paid" + create_table "account_types", id: :serial, force: :cascade do |t| + t.string "name", null: false + t.boolean "is_paid" + t.boolean "is_permanent_paid" t.datetime "created_at" t.datetime "updated_at" end - create_table "accounts", force: :cascade do |t| - t.integer "member_id", null: false - t.integer "account_type_id" + create_table "accounts", id: :serial, force: :cascade do |t| + t.integer "member_id", null: false + t.integer "account_type_id" t.datetime "paid_until" t.datetime "created_at" t.datetime "updated_at" end - create_table "alternate_names", force: :cascade do |t| - t.string "name", null: false - t.integer "crop_id", null: false - t.integer "creator_id", null: false + create_table "alternate_names", id: :serial, force: :cascade do |t| + t.string "name", null: false + t.integer "crop_id", null: false + t.integer "creator_id", null: false t.datetime "created_at" t.datetime "updated_at" end - create_table "authentications", force: :cascade do |t| - t.integer "member_id", null: false - t.string "provider", null: false - t.string "uid" - t.string "token" - t.string "secret" + create_table "authentications", id: :serial, force: :cascade do |t| + t.integer "member_id", null: false + t.string "provider", null: false + t.string "uid" + t.string "token" + t.string "secret" t.datetime "created_at" t.datetime "updated_at" - t.string "name" + t.string "name" + t.index ["member_id"], name: "index_authentications_on_member_id" end - add_index "authentications", ["member_id"], name: "index_authentications_on_member_id", using: :btree - - create_table "comfy_cms_blocks", force: :cascade do |t| - t.string "identifier", null: false - t.text "content" - t.integer "blockable_id" - t.string "blockable_type" + create_table "comfy_cms_blocks", id: :serial, force: :cascade do |t| + t.string "identifier", null: false + t.text "content" + t.integer "blockable_id" + t.string "blockable_type" t.datetime "created_at" t.datetime "updated_at" + t.index ["blockable_id", "blockable_type"], name: "index_comfy_cms_blocks_on_blockable_id_and_blockable_type" + t.index ["identifier"], name: "index_comfy_cms_blocks_on_identifier" end - add_index "comfy_cms_blocks", ["blockable_id", "blockable_type"], name: "index_comfy_cms_blocks_on_blockable_id_and_blockable_type", using: :btree - add_index "comfy_cms_blocks", ["identifier"], name: "index_comfy_cms_blocks_on_identifier", using: :btree - - create_table "comfy_cms_categories", force: :cascade do |t| - t.integer "site_id", null: false - t.string "label", null: false - t.string "categorized_type", null: false + create_table "comfy_cms_categories", id: :serial, force: :cascade do |t| + t.integer "site_id", null: false + t.string "label", null: false + t.string "categorized_type", null: false + t.index ["site_id", "categorized_type", "label"], name: "index_cms_categories_on_site_id_and_cat_type_and_label", unique: true end - add_index "comfy_cms_categories", ["site_id", "categorized_type", "label"], name: "index_cms_categories_on_site_id_and_cat_type_and_label", unique: true, using: :btree - - create_table "comfy_cms_categorizations", force: :cascade do |t| - t.integer "category_id", null: false - t.string "categorized_type", null: false - t.integer "categorized_id", null: false + create_table "comfy_cms_categorizations", id: :serial, force: :cascade do |t| + t.integer "category_id", null: false + t.string "categorized_type", null: false + t.integer "categorized_id", null: false + t.index ["category_id", "categorized_type", "categorized_id"], name: "index_cms_categorizations_on_cat_id_and_catd_type_and_catd_id", unique: true end - add_index "comfy_cms_categorizations", ["category_id", "categorized_type", "categorized_id"], name: "index_cms_categorizations_on_cat_id_and_catd_type_and_catd_id", unique: true, using: :btree - - create_table "comfy_cms_files", force: :cascade do |t| - t.integer "site_id", null: false - t.integer "block_id" - t.string "label", null: false - t.string "file_file_name", null: false - t.string "file_content_type", null: false - t.integer "file_file_size", null: false - t.string "description", limit: 2048 - t.integer "position", default: 0, null: false + create_table "comfy_cms_files", id: :serial, force: :cascade do |t| + t.integer "site_id", null: false + t.integer "block_id" + t.string "label", null: false + t.string "file_file_name", null: false + t.string "file_content_type", null: false + t.integer "file_file_size", null: false + t.string "description", limit: 2048 + t.integer "position", default: 0, null: false t.datetime "created_at" t.datetime "updated_at" + t.index ["site_id", "block_id"], name: "index_comfy_cms_files_on_site_id_and_block_id" + t.index ["site_id", "file_file_name"], name: "index_comfy_cms_files_on_site_id_and_file_file_name" + t.index ["site_id", "label"], name: "index_comfy_cms_files_on_site_id_and_label" + t.index ["site_id", "position"], name: "index_comfy_cms_files_on_site_id_and_position" end - add_index "comfy_cms_files", ["site_id", "block_id"], name: "index_comfy_cms_files_on_site_id_and_block_id", using: :btree - add_index "comfy_cms_files", ["site_id", "file_file_name"], name: "index_comfy_cms_files_on_site_id_and_file_file_name", using: :btree - add_index "comfy_cms_files", ["site_id", "label"], name: "index_comfy_cms_files_on_site_id_and_label", using: :btree - add_index "comfy_cms_files", ["site_id", "position"], name: "index_comfy_cms_files_on_site_id_and_position", using: :btree - - create_table "comfy_cms_layouts", force: :cascade do |t| - t.integer "site_id", null: false - t.integer "parent_id" - t.string "app_layout" - t.string "label", null: false - t.string "identifier", null: false - t.text "content" - t.text "css" - t.text "js" - t.integer "position", default: 0, null: false - t.boolean "is_shared", default: false, null: false + create_table "comfy_cms_layouts", id: :serial, force: :cascade do |t| + t.integer "site_id", null: false + t.integer "parent_id" + t.string "app_layout" + t.string "label", null: false + t.string "identifier", null: false + t.text "content" + t.text "css" + t.text "js" + t.integer "position", default: 0, null: false + t.boolean "is_shared", default: false, null: false t.datetime "created_at" t.datetime "updated_at" + t.index ["parent_id", "position"], name: "index_comfy_cms_layouts_on_parent_id_and_position" + t.index ["site_id", "identifier"], name: "index_comfy_cms_layouts_on_site_id_and_identifier", unique: true end - add_index "comfy_cms_layouts", ["parent_id", "position"], name: "index_comfy_cms_layouts_on_parent_id_and_position", using: :btree - add_index "comfy_cms_layouts", ["site_id", "identifier"], name: "index_comfy_cms_layouts_on_site_id_and_identifier", unique: true, using: :btree - - create_table "comfy_cms_pages", force: :cascade do |t| - t.integer "site_id", null: false - t.integer "layout_id" - t.integer "parent_id" - t.integer "target_page_id" - t.string "label", null: false - t.string "slug" - t.string "full_path", null: false - t.text "content_cache" - t.integer "position", default: 0, null: false - t.integer "children_count", default: 0, null: false - t.boolean "is_published", default: true, null: false - t.boolean "is_shared", default: false, null: false + create_table "comfy_cms_pages", id: :serial, force: :cascade do |t| + t.integer "site_id", null: false + t.integer "layout_id" + t.integer "parent_id" + t.integer "target_page_id" + t.string "label", null: false + t.string "slug" + t.string "full_path", null: false + t.text "content_cache" + t.integer "position", default: 0, null: false + t.integer "children_count", default: 0, null: false + t.boolean "is_published", default: true, null: false + t.boolean "is_shared", default: false, null: false t.datetime "created_at" t.datetime "updated_at" + t.index ["parent_id", "position"], name: "index_comfy_cms_pages_on_parent_id_and_position" + t.index ["site_id", "full_path"], name: "index_comfy_cms_pages_on_site_id_and_full_path" end - add_index "comfy_cms_pages", ["parent_id", "position"], name: "index_comfy_cms_pages_on_parent_id_and_position", using: :btree - add_index "comfy_cms_pages", ["site_id", "full_path"], name: "index_comfy_cms_pages_on_site_id_and_full_path", using: :btree - - create_table "comfy_cms_revisions", force: :cascade do |t| - t.string "record_type", null: false - t.integer "record_id", null: false - t.text "data" + create_table "comfy_cms_revisions", id: :serial, force: :cascade do |t| + t.string "record_type", null: false + t.integer "record_id", null: false + t.text "data" t.datetime "created_at" + t.index ["record_type", "record_id", "created_at"], name: "index_cms_revisions_on_rtype_and_rid_and_created_at" end - add_index "comfy_cms_revisions", ["record_type", "record_id", "created_at"], name: "index_cms_revisions_on_rtype_and_rid_and_created_at", using: :btree - - create_table "comfy_cms_sites", force: :cascade do |t| - t.string "label", null: false - t.string "identifier", null: false - t.string "hostname", null: false - t.string "path" - t.string "locale", default: "en", null: false + create_table "comfy_cms_sites", id: :serial, force: :cascade do |t| + t.string "label", null: false + t.string "identifier", null: false + t.string "hostname", null: false + t.string "path" + t.string "locale", default: "en", null: false t.boolean "is_mirrored", default: false, null: false + t.index ["hostname"], name: "index_comfy_cms_sites_on_hostname" + t.index ["is_mirrored"], name: "index_comfy_cms_sites_on_is_mirrored" end - add_index "comfy_cms_sites", ["hostname"], name: "index_comfy_cms_sites_on_hostname", using: :btree - add_index "comfy_cms_sites", ["is_mirrored"], name: "index_comfy_cms_sites_on_is_mirrored", using: :btree + create_table "comfy_cms_snippets", id: :serial, force: :cascade do |t| + t.integer "site_id", null: false + t.string "label", null: false + t.string "identifier", null: false + t.text "content" + t.integer "position", default: 0, null: false + t.boolean "is_shared", default: false, null: false + t.datetime "created_at" + t.datetime "updated_at" + t.index ["site_id", "identifier"], name: "index_comfy_cms_snippets_on_site_id_and_identifier", unique: true + t.index ["site_id", "position"], name: "index_comfy_cms_snippets_on_site_id_and_position" + end - create_table "comfy_cms_snippets", force: :cascade do |t| - t.integer "site_id", null: false - t.string "label", null: false - t.string "identifier", null: false - t.text "content" - t.integer "position", default: 0, null: false - t.boolean "is_shared", default: false, null: false + create_table "comments", id: :serial, force: :cascade do |t| + t.integer "post_id", null: false + t.integer "author_id", null: false + t.text "body", null: false t.datetime "created_at" t.datetime "updated_at" end - add_index "comfy_cms_snippets", ["site_id", "identifier"], name: "index_comfy_cms_snippets_on_site_id_and_identifier", unique: true, using: :btree - add_index "comfy_cms_snippets", ["site_id", "position"], name: "index_comfy_cms_snippets_on_site_id_and_position", using: :btree - - create_table "comments", force: :cascade do |t| - t.integer "post_id", null: false - t.integer "author_id", null: false - t.text "body", null: false + create_table "crops", id: :serial, force: :cascade do |t| + t.string "name", null: false + t.string "en_wikipedia_url" t.datetime "created_at" t.datetime "updated_at" + t.string "slug" + t.integer "parent_id" + t.integer "plantings_count", default: 0 + t.integer "creator_id" + t.integer "requester_id" + t.string "approval_status", default: "approved" + t.text "reason_for_rejection" + t.text "request_notes" + t.text "rejection_notes" + t.boolean "perennial", default: false + t.integer "median_lifespan" + t.integer "median_days_to_first_harvest" + t.integer "median_days_to_last_harvest" + t.index ["name"], name: "index_crops_on_name" + t.index ["requester_id"], name: "index_crops_on_requester_id" + t.index ["slug"], name: "index_crops_on_slug", unique: true end - create_table "crops", force: :cascade do |t| - t.string "name", null: false - t.string "en_wikipedia_url" - t.datetime "created_at" - t.datetime "updated_at" - t.string "slug" - t.integer "parent_id" - t.integer "plantings_count", default: 0 - t.integer "creator_id" - t.integer "requester_id" - t.string "approval_status", default: "approved" - t.text "reason_for_rejection" - t.text "request_notes" - t.text "rejection_notes" - t.boolean "perennial", default: false - t.integer "median_lifespan" - t.integer "median_days_to_first_harvest" - t.integer "median_days_to_last_harvest" - end - - add_index "crops", ["name"], name: "index_crops_on_name", using: :btree - add_index "crops", ["requester_id"], name: "index_crops_on_requester_id", using: :btree - add_index "crops", ["slug"], name: "index_crops_on_slug", unique: true, using: :btree - create_table "crops_posts", id: false, force: :cascade do |t| t.integer "crop_id" t.integer "post_id" + t.index ["crop_id", "post_id"], name: "index_crops_posts_on_crop_id_and_post_id" + t.index ["crop_id"], name: "index_crops_posts_on_crop_id" end - add_index "crops_posts", ["crop_id", "post_id"], name: "index_crops_posts_on_crop_id_and_post_id", using: :btree - add_index "crops_posts", ["crop_id"], name: "index_crops_posts_on_crop_id", using: :btree - - create_table "follows", force: :cascade do |t| - t.integer "follower_id" - t.integer "followed_id" + create_table "follows", id: :serial, force: :cascade do |t| + t.integer "follower_id" + t.integer "followed_id" t.datetime "created_at" t.datetime "updated_at" end - create_table "forums", force: :cascade do |t| - t.string "name", null: false - t.text "description", null: false - t.integer "owner_id", null: false + create_table "forums", id: :serial, force: :cascade do |t| + t.string "name", null: false + t.text "description", null: false + t.integer "owner_id", null: false t.datetime "created_at" t.datetime "updated_at" - t.string "slug" + t.string "slug" + t.index ["slug"], name: "index_forums_on_slug", unique: true end - add_index "forums", ["slug"], name: "index_forums_on_slug", unique: true, using: :btree - - create_table "gardens", force: :cascade do |t| - t.string "name", null: false - t.integer "owner_id" - t.string "slug", null: false + create_table "gardens", id: :serial, force: :cascade do |t| + t.string "name", null: false + t.integer "owner_id" + t.string "slug", null: false t.datetime "created_at" t.datetime "updated_at" - t.text "description" - t.boolean "active", default: true - t.string "location" - t.float "latitude" - t.float "longitude" - t.decimal "area" - t.string "area_unit" + t.text "description" + t.boolean "active", default: true + t.string "location" + t.float "latitude" + t.float "longitude" + t.decimal "area" + t.string "area_unit" + t.index ["owner_id"], name: "index_gardens_on_owner_id" + t.index ["slug"], name: "index_gardens_on_slug", unique: true end - add_index "gardens", ["owner_id"], name: "index_gardens_on_owner_id", using: :btree - add_index "gardens", ["slug"], name: "index_gardens_on_slug", unique: true, using: :btree - create_table "gardens_photos", id: false, force: :cascade do |t| t.integer "photo_id" t.integer "garden_id" + t.index ["garden_id", "photo_id"], name: "index_gardens_photos_on_garden_id_and_photo_id" end - add_index "gardens_photos", ["garden_id", "photo_id"], name: "index_gardens_photos_on_garden_id_and_photo_id", using: :btree - - create_table "harvests", force: :cascade do |t| - t.integer "crop_id", null: false - t.integer "owner_id", null: false - t.date "harvested_at" - t.decimal "quantity" - t.string "unit" - t.text "description" + create_table "harvests", id: :serial, force: :cascade do |t| + t.integer "crop_id", null: false + t.integer "owner_id", null: false + t.date "harvested_at" + t.decimal "quantity" + t.string "unit" + t.text "description" t.datetime "created_at" t.datetime "updated_at" - t.string "slug" - t.decimal "weight_quantity" - t.string "weight_unit" - t.integer "plant_part_id" - t.float "si_weight" - t.integer "planting_id" + t.string "slug" + t.decimal "weight_quantity" + t.string "weight_unit" + t.integer "plant_part_id" + t.float "si_weight" + t.integer "planting_id" + t.index ["planting_id"], name: "index_harvests_on_planting_id" end - add_index "harvests", ["planting_id"], name: "index_harvests_on_planting_id", using: :btree - create_table "harvests_photos", id: false, force: :cascade do |t| t.integer "photo_id" t.integer "harvest_id" + t.index ["harvest_id", "photo_id"], name: "index_harvests_photos_on_harvest_id_and_photo_id" end - add_index "harvests_photos", ["harvest_id", "photo_id"], name: "index_harvests_photos_on_harvest_id_and_photo_id", using: :btree - - create_table "likes", force: :cascade do |t| - t.integer "member_id" - t.integer "likeable_id" - t.string "likeable_type" - t.string "categories", array: true + create_table "likes", id: :serial, force: :cascade do |t| + t.integer "member_id" + t.integer "likeable_id" + t.string "likeable_type" + t.string "categories", array: true t.datetime "created_at" t.datetime "updated_at" + t.index ["likeable_id"], name: "index_likes_on_likeable_id" + t.index ["likeable_type", "likeable_id"], name: "index_likes_on_likeable_type_and_likeable_id" + t.index ["member_id"], name: "index_likes_on_member_id" end - add_index "likes", ["likeable_id"], name: "index_likes_on_likeable_id", using: :btree - add_index "likes", ["likeable_type", "likeable_id"], name: "index_likes_on_likeable_type_and_likeable_id", using: :btree - add_index "likes", ["member_id"], name: "index_likes_on_member_id", using: :btree - - create_table "median_functions", force: :cascade do |t| + create_table "median_functions", id: :serial, force: :cascade do |t| end - create_table "members", force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false - t.string "reset_password_token" + create_table "members", id: :serial, force: :cascade do |t| + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0 + t.integer "sign_in_count", default: 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" - t.string "current_sign_in_ip" - t.string "last_sign_in_ip" - t.string "confirmation_token" + t.string "current_sign_in_ip" + t.string "last_sign_in_ip" + t.string "confirmation_token" t.datetime "confirmed_at" t.datetime "confirmation_sent_at" - t.string "unconfirmed_email" - t.integer "failed_attempts", default: 0 - t.string "unlock_token" + t.string "unconfirmed_email" + t.integer "failed_attempts", default: 0 + t.string "unlock_token" t.datetime "locked_at" t.datetime "created_at" t.datetime "updated_at" - t.string "login_name" - t.string "slug" - t.boolean "tos_agreement" - t.boolean "show_email" - t.string "location" - t.float "latitude" - t.float "longitude" - t.boolean "send_notification_email", default: true - t.text "bio" - t.integer "plantings_count" - t.boolean "newsletter" - t.boolean "send_planting_reminder", default: true - t.string "preferred_avatar_uri" - t.integer "gardens_count" - t.integer "harvests_count" - t.integer "seeds_count" + t.string "login_name" + t.string "slug" + t.boolean "tos_agreement" + t.boolean "show_email" + t.string "location" + t.float "latitude" + t.float "longitude" + t.boolean "send_notification_email", default: true + t.text "bio" + t.integer "plantings_count" + t.boolean "newsletter" + t.boolean "send_planting_reminder", default: true + t.string "preferred_avatar_uri" + t.integer "gardens_count" + t.integer "harvests_count" + t.integer "seeds_count" t.datetime "deleted_at" + t.index ["confirmation_token"], name: "index_members_on_confirmation_token", unique: true + t.index ["deleted_at"], name: "index_members_on_deleted_at" + t.index ["email"], name: "index_members_on_email", unique: true + t.index ["reset_password_token"], name: "index_members_on_reset_password_token", unique: true + t.index ["slug"], name: "index_members_on_slug", unique: true + t.index ["unlock_token"], name: "index_members_on_unlock_token", unique: true end - add_index "members", ["confirmation_token"], name: "index_members_on_confirmation_token", unique: true, using: :btree - add_index "members", ["deleted_at"], name: "index_members_on_deleted_at", using: :btree - add_index "members", ["email"], name: "index_members_on_email", unique: true, using: :btree - add_index "members", ["reset_password_token"], name: "index_members_on_reset_password_token", unique: true, using: :btree - add_index "members", ["slug"], name: "index_members_on_slug", unique: true, using: :btree - add_index "members", ["unlock_token"], name: "index_members_on_unlock_token", unique: true, using: :btree - create_table "members_roles", id: false, force: :cascade do |t| t.integer "member_id" t.integer "role_id" end - create_table "notifications", force: :cascade do |t| - t.integer "sender_id" - t.integer "recipient_id", null: false - t.string "subject" - t.text "body" - t.boolean "read", default: false - t.integer "post_id" + create_table "notifications", id: :serial, force: :cascade do |t| + t.integer "sender_id" + t.integer "recipient_id", null: false + t.string "subject" + t.text "body" + t.boolean "read", default: false + t.integer "post_id" t.datetime "created_at" t.datetime "updated_at" end - create_table "order_items", force: :cascade do |t| - t.integer "order_id" - t.integer "product_id" - t.integer "price" - t.integer "quantity" + create_table "order_items", id: :serial, force: :cascade do |t| + t.integer "order_id" + t.integer "product_id" + t.integer "price" + t.integer "quantity" t.datetime "created_at" t.datetime "updated_at" end - create_table "orders", force: :cascade do |t| + create_table "orders", id: :serial, force: :cascade do |t| t.datetime "created_at" t.datetime "updated_at" t.datetime "completed_at" - t.integer "member_id" - t.string "paypal_express_token" - t.string "paypal_express_payer_id" - t.string "referral_code" + t.integer "member_id" + t.string "paypal_express_token" + t.string "paypal_express_payer_id" + t.string "referral_code" end create_table "orders_products", id: false, force: :cascade do |t| @@ -383,28 +363,27 @@ ActiveRecord::Schema.define(version: 20171129041341) do t.integer "product_id" end - create_table "photographings", force: :cascade do |t| - t.integer "photo_id", null: false - t.integer "photographable_id", null: false - t.string "photographable_type", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + create_table "photographings", id: :serial, force: :cascade do |t| + t.integer "photo_id", null: false + t.integer "photographable_id", null: false + t.string "photographable_type", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["photographable_id", "photographable_type", "photo_id"], name: "items_to_photos_idx", unique: true + t.index ["photographable_id", "photographable_type"], name: "photographable_idx" end - add_index "photographings", ["photographable_id", "photographable_type", "photo_id"], name: "items_to_photos_idx", unique: true, using: :btree - add_index "photographings", ["photographable_id", "photographable_type"], name: "photographable_idx", using: :btree - - create_table "photos", force: :cascade do |t| - t.integer "owner_id", null: false - t.string "thumbnail_url", null: false - t.string "fullsize_url", null: false + create_table "photos", id: :serial, force: :cascade do |t| + t.integer "owner_id", null: false + t.string "thumbnail_url", null: false + t.string "fullsize_url", null: false t.datetime "created_at" t.datetime "updated_at" - t.string "title", null: false - t.string "license_name", null: false - t.string "license_url" - t.string "link_url", null: false - t.string "flickr_photo_id" + t.string "title", null: false + t.string "license_name", null: false + t.string "license_url" + t.string "link_url", null: false + t.string "flickr_photo_id" end create_table "photos_plantings", id: false, force: :cascade do |t| @@ -415,99 +394,97 @@ ActiveRecord::Schema.define(version: 20171129041341) do create_table "photos_seeds", id: false, force: :cascade do |t| t.integer "photo_id" t.integer "seed_id" + t.index ["seed_id", "photo_id"], name: "index_photos_seeds_on_seed_id_and_photo_id" end - add_index "photos_seeds", ["seed_id", "photo_id"], name: "index_photos_seeds_on_seed_id_and_photo_id", using: :btree - - create_table "plant_parts", force: :cascade do |t| - t.string "name" + create_table "plant_parts", id: :serial, force: :cascade do |t| + t.string "name" t.datetime "created_at" t.datetime "updated_at" - t.string "slug" + t.string "slug" end - create_table "plantings", force: :cascade do |t| - t.integer "garden_id", null: false - t.integer "crop_id", null: false - t.date "planted_at" - t.integer "quantity" - t.text "description" + create_table "plantings", id: :serial, force: :cascade do |t| + t.integer "garden_id", null: false + t.integer "crop_id", null: false + t.date "planted_at" + t.integer "quantity" + t.text "description" t.datetime "created_at" t.datetime "updated_at" - t.string "slug" - t.string "sunniness" - t.string "planted_from" - t.integer "owner_id" - t.boolean "finished", default: false - t.date "finished_at" - t.integer "lifespan" - t.integer "days_to_first_harvest" - t.integer "days_to_last_harvest" + t.string "slug" + t.string "sunniness" + t.string "planted_from" + t.integer "owner_id" + t.boolean "finished", default: false + t.date "finished_at" + t.integer "lifespan" + t.integer "days_to_first_harvest" + t.integer "days_to_last_harvest" + t.boolean "failed" + t.text "failure_cause" + t.index ["failed"], name: "index_plantings_on_failed" + t.index ["slug"], name: "index_plantings_on_slug", unique: true end - add_index "plantings", ["slug"], name: "index_plantings_on_slug", unique: true, using: :btree - - create_table "posts", force: :cascade do |t| - t.integer "author_id", null: false - t.string "subject", null: false - t.text "body", null: false + create_table "posts", id: :serial, force: :cascade do |t| + t.integer "author_id", null: false + t.string "subject", null: false + t.text "body", null: false t.datetime "created_at" t.datetime "updated_at" - t.string "slug" - t.integer "forum_id" + t.string "slug" + t.integer "forum_id" + t.index ["created_at", "author_id"], name: "index_posts_on_created_at_and_author_id" + t.index ["slug"], name: "index_posts_on_slug", unique: true end - add_index "posts", ["created_at", "author_id"], name: "index_posts_on_created_at_and_author_id", using: :btree - add_index "posts", ["slug"], name: "index_posts_on_slug", unique: true, using: :btree - - create_table "products", force: :cascade do |t| - t.string "name", null: false - t.text "description", null: false - t.integer "min_price", null: false + create_table "products", id: :serial, force: :cascade do |t| + t.string "name", null: false + t.text "description", null: false + t.integer "min_price", null: false t.datetime "created_at" t.datetime "updated_at" - t.integer "account_type_id" - t.integer "paid_months" - t.integer "recommended_price" + t.integer "account_type_id" + t.integer "paid_months" + t.integer "recommended_price" end - create_table "roles", force: :cascade do |t| - t.string "name", null: false - t.text "description" + create_table "roles", id: :serial, force: :cascade do |t| + t.string "name", null: false + t.text "description" t.datetime "created_at" t.datetime "updated_at" - t.string "slug" + t.string "slug" + t.index ["slug"], name: "index_roles_on_slug", unique: true end - add_index "roles", ["slug"], name: "index_roles_on_slug", unique: true, using: :btree - - create_table "scientific_names", force: :cascade do |t| - t.string "name", null: false - t.integer "crop_id", null: false + create_table "scientific_names", id: :serial, force: :cascade do |t| + t.string "name", null: false + t.integer "crop_id", null: false t.datetime "created_at" t.datetime "updated_at" - t.integer "creator_id" + t.integer "creator_id" end - create_table "seeds", force: :cascade do |t| - t.integer "owner_id", null: false - t.integer "crop_id", null: false - t.text "description" - t.integer "quantity" - t.date "plant_before" + create_table "seeds", id: :serial, force: :cascade do |t| + t.integer "owner_id", null: false + t.integer "crop_id", null: false + t.text "description" + t.integer "quantity" + t.date "plant_before" t.datetime "created_at" t.datetime "updated_at" - t.string "tradable_to", default: "nowhere" - t.string "slug" - t.integer "days_until_maturity_min" - t.integer "days_until_maturity_max" - t.text "organic", default: "unknown" - t.text "gmo", default: "unknown" - t.text "heirloom", default: "unknown" + t.string "tradable_to", default: "nowhere" + t.string "slug" + t.integer "days_until_maturity_min" + t.integer "days_until_maturity_max" + t.text "organic", default: "unknown" + t.text "gmo", default: "unknown" + t.text "heirloom", default: "unknown" + t.index ["slug"], name: "index_seeds_on_slug", unique: true end - add_index "seeds", ["slug"], name: "index_seeds_on_slug", unique: true, using: :btree - add_foreign_key "harvests", "plantings" add_foreign_key "photographings", "photos" end diff --git a/db/seeds.rb b/db/seeds.rb index 7df7e0e4c..a1477fbe6 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -134,7 +134,7 @@ end def create_cropbot @cropbot_user = Member.new( login_name: "cropbot", - email: Growstuff::Application.config.bot_email, + email: Rails.application.config.bot_email, password: SecureRandom.urlsafe_base64(64), tos_agreement: true ) diff --git a/lib/haml/filters/growstuff_markdown.rb b/lib/haml/filters/growstuff_markdown.rb index b7b257b53..51d95d1fe 100644 --- a/lib/haml/filters/growstuff_markdown.rb +++ b/lib/haml/filters/growstuff_markdown.rb @@ -17,7 +17,7 @@ module Haml::Filters # rubocop:disable Style/ClassAndModuleChildren MEMBER_REGEX = /(?#{name}" if name "#{crop.name}" end diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 1911ae8da..c538ad38d 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -29,7 +29,7 @@ describe 'member' do end it "should have a default-type account by default" do - member.account.account_type.name.should eq Growstuff::Application.config.default_account_type + member.account.account_type.name.should eq Rails.application.config.default_account_type member.paid?.should be(false) end diff --git a/spec/views/layouts/application_spec.rb b/spec/views/layouts/application_spec.rb index f094ae2c2..e706bc575 100644 --- a/spec/views/layouts/application_spec.rb +++ b/spec/views/layouts/application_spec.rb @@ -6,7 +6,7 @@ describe 'layouts/application.html.haml', type: "view" do end it 'includes the analytics code' do - Growstuff::Application.config.analytics_code = '' + Rails.application.config.analytics_code = '' render assert_select "script", text: 'alert("foo!")' rendered.should_not have_content 'script' diff --git a/spec/views/orders/show.html.haml_spec.rb b/spec/views/orders/show.html.haml_spec.rb index a9f662de9..4f9c7d7f6 100644 --- a/spec/views/orders/show.html.haml_spec.rb +++ b/spec/views/orders/show.html.haml_spec.rb @@ -31,7 +31,7 @@ describe "orders/show" do end it "shows a foreign exchange link for the total" do - currency = Growstuff::Application.config.currency + currency = Rails.application.config.currency assert_select("a[href='http://www.wolframalpha.com/input/?i=198.00+#{currency}']") end diff --git a/spec/views/shop/index_spec.rb b/spec/views/shop/index_spec.rb index 754f05c09..29b740c92 100644 --- a/spec/views/shop/index_spec.rb +++ b/spec/views/shop/index_spec.rb @@ -20,20 +20,20 @@ describe 'shop/index.html.haml', type: "view" do end it 'shows prices in configured currency' do - rendered.should have_content format('9.99 %s', Growstuff::Application.config.currency) + rendered.should have_content format('9.99 %s', Rails.application.config.currency) end it 'should contain an exchange rate link' do - currency = Growstuff::Application.config.currency + currency = Rails.application.config.currency assert_select("a[href='http://www.wolframalpha.com/input/?i=9.99+#{currency}']") end it 'shows recommended price for products that have it' do - rendered.should have_content format('12.00 %s', Growstuff::Application.config.currency) + rendered.should have_content format('12.00 %s', Rails.application.config.currency) end it 'should contain an exchange rate link for recommended price' do - currency = Growstuff::Application.config.currency + currency = Rails.application.config.currency assert_select("a[href='http://www.wolframalpha.com/input/?i=12.00+#{currency}']") end From b6c9b13d4d7ea1a0e44cabbecd26b4ec0e78cf1a Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 17:16:14 +1300 Subject: [PATCH 023/219] Models inherit from application record --- app/models/account.rb | 2 +- app/models/account_type.rb | 2 +- app/models/alternate_name.rb | 2 +- app/models/application_record.rb | 3 +++ app/models/authentication.rb | 2 +- app/models/comment.rb | 2 +- app/models/crop.rb | 2 +- app/models/follow.rb | 2 +- app/models/forum.rb | 2 +- app/models/garden.rb | 2 +- app/models/harvest.rb | 2 +- app/models/like.rb | 2 +- app/models/member.rb | 2 +- app/models/notification.rb | 2 +- app/models/order.rb | 2 +- app/models/order_item.rb | 2 +- app/models/photo.rb | 2 +- app/models/photographing.rb | 2 +- app/models/plant_part.rb | 2 +- app/models/planting.rb | 2 +- app/models/post.rb | 2 +- app/models/product.rb | 2 +- app/models/role.rb | 2 +- app/models/scientific_name.rb | 2 +- app/models/seed.rb | 2 +- db/migrate/20171129041341_create_photographings.rb | 8 ++++---- 26 files changed, 31 insertions(+), 28 deletions(-) create mode 100644 app/models/application_record.rb diff --git a/app/models/account.rb b/app/models/account.rb index be14f406a..79d5327b7 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -1,4 +1,4 @@ -class Account < ActiveRecord::Base +class Account < ApplicationRecord belongs_to :member belongs_to :account_type diff --git a/app/models/account_type.rb b/app/models/account_type.rb index 06416751a..c2294ca53 100644 --- a/app/models/account_type.rb +++ b/app/models/account_type.rb @@ -1,4 +1,4 @@ -class AccountType < ActiveRecord::Base +class AccountType < ApplicationRecord # # Relationships has_many :products diff --git a/app/models/alternate_name.rb b/app/models/alternate_name.rb index aa960e1f4..0d7f12198 100644 --- a/app/models/alternate_name.rb +++ b/app/models/alternate_name.rb @@ -1,4 +1,4 @@ -class AlternateName < ActiveRecord::Base +class AlternateName < ApplicationRecord after_commit { |an| an.crop.__elasticsearch__.index_document if an.crop && ENV['GROWSTUFF_ELASTICSEARCH'] == "true" } belongs_to :crop belongs_to :creator, class_name: 'Member' diff --git a/app/models/application_record.rb b/app/models/application_record.rb new file mode 100644 index 000000000..10a4cba84 --- /dev/null +++ b/app/models/application_record.rb @@ -0,0 +1,3 @@ +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end diff --git a/app/models/authentication.rb b/app/models/authentication.rb index e6171c8b7..91ecc8de9 100644 --- a/app/models/authentication.rb +++ b/app/models/authentication.rb @@ -1,3 +1,3 @@ -class Authentication < ActiveRecord::Base +class Authentication < ApplicationRecord belongs_to :member end diff --git a/app/models/comment.rb b/app/models/comment.rb index 7098e0b46..59d7102ad 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,4 +1,4 @@ -class Comment < ActiveRecord::Base +class Comment < ApplicationRecord belongs_to :author, class_name: 'Member' belongs_to :post diff --git a/app/models/crop.rb b/app/models/crop.rb index 15787f6f1..a961daf58 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -1,4 +1,4 @@ -class Crop < ActiveRecord::Base +class Crop < ApplicationRecord extend FriendlyId friendly_id :name, use: %i(slugged finders) diff --git a/app/models/follow.rb b/app/models/follow.rb index 557887cb6..34028857b 100644 --- a/app/models/follow.rb +++ b/app/models/follow.rb @@ -1,4 +1,4 @@ -class Follow < ActiveRecord::Base +class Follow < ApplicationRecord belongs_to :follower, class_name: "Member" belongs_to :followed, class_name: "Member" validates :follower_id, uniqueness: { scope: :followed_id } diff --git a/app/models/forum.rb b/app/models/forum.rb index 7cbc0d43d..2579876ad 100644 --- a/app/models/forum.rb +++ b/app/models/forum.rb @@ -1,4 +1,4 @@ -class Forum < ActiveRecord::Base +class Forum < ApplicationRecord extend FriendlyId validates :name, presence: true friendly_id :name, use: %i(slugged finders) diff --git a/app/models/garden.rb b/app/models/garden.rb index 0c3378b00..096647035 100644 --- a/app/models/garden.rb +++ b/app/models/garden.rb @@ -1,4 +1,4 @@ -class Garden < ActiveRecord::Base +class Garden < ApplicationRecord extend FriendlyId include Geocodable include PhotoCapable diff --git a/app/models/harvest.rb b/app/models/harvest.rb index 6bb9aa048..a5328ef49 100644 --- a/app/models/harvest.rb +++ b/app/models/harvest.rb @@ -1,4 +1,4 @@ -class Harvest < ActiveRecord::Base +class Harvest < ApplicationRecord include ActionView::Helpers::NumberHelper extend FriendlyId include PhotoCapable diff --git a/app/models/like.rb b/app/models/like.rb index 8b4cfef9c..05911fc95 100644 --- a/app/models/like.rb +++ b/app/models/like.rb @@ -1,4 +1,4 @@ -class Like < ActiveRecord::Base +class Like < ApplicationRecord belongs_to :member belongs_to :likeable, polymorphic: true validates :member, :likeable, presence: true diff --git a/app/models/member.rb b/app/models/member.rb index 80a5ba840..654e6b3bd 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -1,4 +1,4 @@ -class Member < ActiveRecord::Base +class Member < ApplicationRecord acts_as_paranoid # implements soft deletion before_destroy :newsletter_unsubscribe include Geocodable diff --git a/app/models/notification.rb b/app/models/notification.rb index 1b09b1e5e..fe3ec8907 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -1,4 +1,4 @@ -class Notification < ActiveRecord::Base +class Notification < ApplicationRecord belongs_to :sender, class_name: 'Member' belongs_to :recipient, class_name: 'Member' belongs_to :post diff --git a/app/models/order.rb b/app/models/order.rb index 53a309c35..68d9e4fb3 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -1,4 +1,4 @@ -class Order < ActiveRecord::Base +class Order < ApplicationRecord # # Relationships belongs_to :member, with_deleted: true diff --git a/app/models/order_item.rb b/app/models/order_item.rb index 380fbff58..771919b55 100644 --- a/app/models/order_item.rb +++ b/app/models/order_item.rb @@ -1,4 +1,4 @@ -class OrderItem < ActiveRecord::Base +class OrderItem < ApplicationRecord belongs_to :order belongs_to :product diff --git a/app/models/photo.rb b/app/models/photo.rb index 5800ec6f9..0daae7825 100644 --- a/app/models/photo.rb +++ b/app/models/photo.rb @@ -1,4 +1,4 @@ -class Photo < ActiveRecord::Base +class Photo < ApplicationRecord belongs_to :owner, class_name: 'Member' PHOTO_CAPABLE = %w(Garden Planting Harvest Seed).freeze diff --git a/app/models/photographing.rb b/app/models/photographing.rb index 3edb78351..5d1ab1073 100644 --- a/app/models/photographing.rb +++ b/app/models/photographing.rb @@ -1,4 +1,4 @@ -class Photographing < ActiveRecord::Base +class Photographing < ApplicationRecord belongs_to :photo belongs_to :photographable, polymorphic: true diff --git a/app/models/plant_part.rb b/app/models/plant_part.rb index e5045baa8..2590fb966 100644 --- a/app/models/plant_part.rb +++ b/app/models/plant_part.rb @@ -1,4 +1,4 @@ -class PlantPart < ActiveRecord::Base +class PlantPart < ApplicationRecord extend FriendlyId friendly_id :name, use: %i(slugged finders) diff --git a/app/models/planting.rb b/app/models/planting.rb index c51166264..74795066c 100644 --- a/app/models/planting.rb +++ b/app/models/planting.rb @@ -1,4 +1,4 @@ -class Planting < ActiveRecord::Base +class Planting < ApplicationRecord extend FriendlyId include PhotoCapable friendly_id :planting_slug, use: %i(slugged finders) diff --git a/app/models/post.rb b/app/models/post.rb index 43885e99d..ae27dbb50 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -1,4 +1,4 @@ -class Post < ActiveRecord::Base +class Post < ApplicationRecord extend FriendlyId include Likeable friendly_id :author_date_subject, use: %i(slugged finders) diff --git a/app/models/product.rb b/app/models/product.rb index 01b5a8902..3946645d3 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -1,4 +1,4 @@ -class Product < ActiveRecord::Base +class Product < ApplicationRecord # # Relationships belongs_to :account_type diff --git a/app/models/role.rb b/app/models/role.rb index 89b940125..3606641ea 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -1,4 +1,4 @@ -class Role < ActiveRecord::Base +class Role < ApplicationRecord extend FriendlyId friendly_id :name, use: %i(slugged finders) diff --git a/app/models/scientific_name.rb b/app/models/scientific_name.rb index bca467b5b..812b696b8 100644 --- a/app/models/scientific_name.rb +++ b/app/models/scientific_name.rb @@ -1,4 +1,4 @@ -class ScientificName < ActiveRecord::Base +class ScientificName < ApplicationRecord after_commit { |sn| sn.crop.__elasticsearch__.index_document if sn.crop && ENV['GROWSTUFF_ELASTICSEARCH'] == "true" } belongs_to :crop belongs_to :creator, class_name: 'Member' diff --git a/app/models/seed.rb b/app/models/seed.rb index 56c9f809d..ca99c4fc3 100644 --- a/app/models/seed.rb +++ b/app/models/seed.rb @@ -1,4 +1,4 @@ -class Seed < ActiveRecord::Base +class Seed < ApplicationRecord extend FriendlyId include PhotoCapable friendly_id :seed_slug, use: %i(slugged finders) diff --git a/db/migrate/20171129041341_create_photographings.rb b/db/migrate/20171129041341_create_photographings.rb index 8054ba9cd..a18ab3680 100644 --- a/db/migrate/20171129041341_create_photographings.rb +++ b/db/migrate/20171129041341_create_photographings.rb @@ -35,19 +35,19 @@ class CreatePhotographings < ActiveRecord::Migration Photographing.create! photo_id: s.photo_id, photographable_id: s.seed_id, photographable_type: 'Seed' end end - class GardensPhoto < ActiveRecord::Base + class GardensPhoto < ApplicationRecord belongs_to :photo belongs_to :garden end - class PhotosPlanting < ActiveRecord::Base + class PhotosPlanting < ApplicationRecord belongs_to :photo belongs_to :planting end - class HarvestsPhoto < ActiveRecord::Base + class HarvestsPhoto < ApplicationRecord belongs_to :photo belongs_to :harvest end - class PhotosSeed < ActiveRecord::Base + class PhotosSeed < ApplicationRecord belongs_to :photo belongs_to :seed end From 65d5b2d61a0f178b6692c044e24bac0a000e0e14 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 17:18:41 +1300 Subject: [PATCH 024/219] Application mailer for rails 5 --- app/mailers/application_mailer.rb | 4 ++++ app/mailers/notifier.rb | 2 +- app/views/layouts/mailer.html.erb | 8 ++++++++ app/views/layouts/mailer.text.erb | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 app/mailers/application_mailer.rb create mode 100644 app/views/layouts/mailer.html.erb create mode 100644 app/views/layouts/mailer.text.erb diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb new file mode 100644 index 000000000..286b2239d --- /dev/null +++ b/app/mailers/application_mailer.rb @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: 'from@example.com' + layout 'mailer' +end diff --git a/app/mailers/notifier.rb b/app/mailers/notifier.rb index 569400ce6..12c20cebe 100644 --- a/app/mailers/notifier.rb +++ b/app/mailers/notifier.rb @@ -1,4 +1,4 @@ -class Notifier < ActionMailer::Base +class Notifier < ApplicationMailer include NotificationsHelper default from: "Growstuff " diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb new file mode 100644 index 000000000..cb2589829 --- /dev/null +++ b/app/views/layouts/mailer.html.erb @@ -0,0 +1,8 @@ + + + + + + + <%= yield %> + \ No newline at end of file diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb new file mode 100644 index 000000000..37f0bddbd --- /dev/null +++ b/app/views/layouts/mailer.text.erb @@ -0,0 +1 @@ +<%= yield %> From 46d56fde319cb6f86e070eede329ce5aab4cf509 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 17:20:08 +1300 Subject: [PATCH 025/219] Application job for Rails 5 --- app/jobs/application_job.rb | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 app/jobs/application_job.rb diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb new file mode 100644 index 000000000..a009ace51 --- /dev/null +++ b/app/jobs/application_job.rb @@ -0,0 +1,2 @@ +class ApplicationJob < ActiveJob::Base +end From 4917ab9ae88ba5a83d9699e81b41b57dcc8be451 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 17:21:40 +1300 Subject: [PATCH 026/219] Fixed order->member relationship, after change to paranoia gem --- app/models/order.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/order.rb b/app/models/order.rb index 68d9e4fb3..fe5f65888 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -1,7 +1,7 @@ class Order < ApplicationRecord # # Relationships - belongs_to :member, with_deleted: true + belongs_to :member, -> { with_deleted } has_many :order_items, dependent: :destroy # From d456d045cd42cec1cd91b56e504a538fdeb54ea3 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 17:24:51 +1300 Subject: [PATCH 027/219] Changed order of photo association relationship declarations --- app/models/concerns/photo_capable.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/concerns/photo_capable.rb b/app/models/concerns/photo_capable.rb index 177a2f832..758569c35 100644 --- a/app/models/concerns/photo_capable.rb +++ b/app/models/concerns/photo_capable.rb @@ -2,8 +2,8 @@ module PhotoCapable extend ActiveSupport::Concern included do - has_many :photos, through: :photographings, as: :photographable has_many :photographings, as: :photographable, dependent: :destroy + has_many :photos, through: :photographings, as: :photographable scope :has_photos, -> { includes(:photos).where.not(photos: { id: nil }) } end From 7bc91e415a5624346350d318aa8638402f29026c Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Wed, 3 Jan 2018 19:09:32 +1300 Subject: [PATCH 028/219] Re-instate active_median gem --- Gemfile | 6 +++--- Gemfile.lock | 11 +++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Gemfile b/Gemfile index a5c7b7dce..3c40d1d38 100644 --- a/Gemfile +++ b/Gemfile @@ -22,11 +22,11 @@ gem 'font-awesome-sass' gem 'uglifier' # JavaScript compressor # planting and harvest predictions -# gem 'active_median' +gem 'active_median' gem 'flickraw' gem 'jquery-rails' -gem 'jquery-ui-rails', '~> 5.0.2' # needs careful upgrade with change of location +gem 'jquery-ui-rails' gem 'js-routes' # provides access to Rails routes in Javascript gem 'cancancan' # for checking member privileges @@ -79,7 +79,7 @@ gem 'omniauth-flickr', '>= 0.0.15' gem 'omniauth-twitter' # For charting data -gem 'd3-rails', '~> 3.5' # 4.* produces Error: : could not find an object to spy upon for linear() - see https://travis-ci.org/Growstuff/growstuff/jobs/204461482 +gem 'd3-rails' # client for Elasticsearch. Elasticsearch is a flexible # and powerful, distributed, real-time search and analytics engine. diff --git a/Gemfile.lock b/Gemfile.lock index bc54de685..db72f11dc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -28,6 +28,8 @@ GEM active_link_to (1.0.5) actionpack addressable + active_median (0.1.4) + activerecord active_merchant-paypal-bogus-gateway (0.1.0) activemerchant active_utils (3.3.9) @@ -132,7 +134,7 @@ GEM crass (1.0.3) csv_shaper (1.3.0) activesupport (>= 3.0.0) - d3-rails (3.5.17) + d3-rails (4.10.2) railties (>= 3.1) dalli (2.7.6) database_cleaner (1.6.2) @@ -254,7 +256,7 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - jquery-ui-rails (5.0.5) + jquery-ui-rails (6.0.1) railties (>= 3.2.16) js-routes (1.4.3) railties (>= 3.2) @@ -520,6 +522,7 @@ PLATFORMS ruby DEPENDENCIES + active_median active_merchant-paypal-bogus-gateway active_utils activemerchant @@ -541,7 +544,7 @@ DEPENDENCIES comfortable_mexican_sofa coveralls csv_shaper - d3-rails (~> 3.5) + d3-rails dalli database_cleaner devise @@ -565,7 +568,7 @@ DEPENDENCIES i18n-tasks jasmine jquery-rails - jquery-ui-rails (~> 5.0.2) + jquery-ui-rails js-routes jsonapi-resources kaminari From 9d2732fffa381356b021e645a781fecc9d38feb8 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 4 Jan 2018 14:29:09 +1300 Subject: [PATCH 029/219] removing comfortable mexican sofa for now --- Gemfile | 2 +- Gemfile.lock | 89 +++++++++++++--------------------------------------- 2 files changed, 23 insertions(+), 68 deletions(-) diff --git a/Gemfile b/Gemfile index 3c40d1d38..eb461c054 100644 --- a/Gemfile +++ b/Gemfile @@ -42,7 +42,7 @@ gem 'pg' gem 'ruby-units' # for unit conversion gem 'unicorn' # http server -gem 'comfortable_mexican_sofa' # content management system +# gem 'comfortable_mexican_sofa' # content management system gem 'bootstrap-kaminari-views' # bootstrap views for kaminari gem 'kaminari' # pagination diff --git a/Gemfile.lock b/Gemfile.lock index db72f11dc..bdd2bc94e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -25,9 +25,6 @@ GEM erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - active_link_to (1.0.5) - actionpack - addressable active_median (0.1.4) activerecord active_merchant-paypal-bogus-gateway (0.1.0) @@ -38,7 +35,7 @@ GEM activejob (5.1.4) activesupport (= 5.1.4) globalid (>= 0.3.6) - activemerchant (1.75.0) + activemerchant (1.76.0) activesupport (>= 3.2.14, < 6.x) builder (>= 2.1.2, < 4.0.0) i18n (>= 0.6.9) @@ -66,9 +63,7 @@ GEM erubi (>= 1.0.0) rack (>= 0.9.0) bluecloth (2.2.0) - bonsai-elasticsearch-rails (0.2.0) - elasticsearch-model (~> 0) - elasticsearch-rails (~> 0) + bonsai-elasticsearch-rails (0.0.4) bootstrap-datepicker-rails (1.7.1.1) railties (>= 3.0) bootstrap-kaminari-views (0.0.5) @@ -98,12 +93,9 @@ GEM launchy childprocess (0.8.0) ffi (~> 1.0, >= 1.0.11) - climate_control (0.2.0) cliver (0.3.2) - cocaine (0.5.8) - climate_control (>= 0.0.3, < 1.0) - codeclimate-test-reporter (1.0.7) - simplecov + codeclimate-test-reporter (1.0.8) + simplecov (<= 0.13) coderay (1.1.2) coffee-rails (4.2.2) coffee-script (>= 2.2.0) @@ -112,25 +104,14 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - comfortable_mexican_sofa (1.8.2) - active_link_to (>= 1.0.0) - coffee-rails (>= 3.1.0) - formatted_form (>= 2.1.0) - haml-rails (>= 0.3.0) - jquery-rails (>= 3.0.0) - jquery-ui-rails (>= 4.0.0) - paperclip (>= 3.4.0) - rails (>= 3.1.0) - redcarpet (>= 2.2.0) - sass-rails (>= 3.1.0) concurrent-ruby (1.0.5) connection_pool (2.2.1) - coveralls (0.7.1) - multi_json (~> 1.3) - rest-client - simplecov (>= 0.7) - term-ansicolor - thor + coveralls (0.8.19) + json (>= 1.8, < 3) + simplecov (~> 0.12.0) + term-ansicolor (~> 1.3) + thor (~> 0.19.1) + tins (~> 1.6) crass (1.0.3) csv_shaper (1.3.0) activesupport (>= 3.0.0) @@ -146,23 +127,21 @@ GEM warden (~> 1.2.3) diff-lcs (1.3) docile (1.1.5) - domain_name (0.5.20170404) - unf (>= 0.0.5, < 1.0.0) easy_translate (0.5.0) json thread thread_safe - elasticsearch (6.0.0) - elasticsearch-api (= 6.0.0) - elasticsearch-transport (= 6.0.0) - elasticsearch-api (6.0.0) + elasticsearch (5.0.4) + elasticsearch-api (= 5.0.4) + elasticsearch-transport (= 5.0.4) + elasticsearch-api (5.0.4) multi_json - elasticsearch-model (0.1.9) + elasticsearch-model (5.0.2) activesupport (> 3) - elasticsearch (> 0.4) + elasticsearch (~> 5) hashie - elasticsearch-rails (0.1.9) - elasticsearch-transport (6.0.0) + elasticsearch-rails (5.0.2) + elasticsearch-transport (5.0.4) faraday multi_json erubi (1.7.0) @@ -184,8 +163,6 @@ GEM flickraw (0.9.9) font-awesome-sass (4.7.0) sass (>= 3.2) - formatted_form (2.1.2) - rails (>= 3.1) friendly_id (5.2.3) activerecord (>= 4.0.0) geocoder (1.4.5) @@ -230,8 +207,6 @@ GEM haml (>= 4.0, < 6) nokogiri (>= 1.6.0) ruby_parser (~> 3.5) - http-cookie (1.0.3) - domain_name (~> 0.5) httparty (0.15.6) multi_xml (>= 0.5.2) i18n (0.9.1) @@ -297,10 +272,6 @@ GEM mini_mime (>= 0.1.1) memcachier (0.0.2) method_source (0.9.0) - mime-types (3.1) - mime-types-data (~> 3.2015) - mime-types-data (3.2016.0521) - mimemagic (0.3.2) mini_mime (1.0.0) mini_portile2 (2.3.0) minitest (5.11.1) @@ -308,7 +279,6 @@ GEM multi_json (1.11.3) multi_xml (0.6.0) multipart-post (2.0.0) - netrc (0.11.0) newrelic_rpm (4.7.1.340) nio4r (2.2.0) nokogiri (1.8.1) @@ -338,12 +308,6 @@ GEM omniauth-oauth (~> 1.1) rack orm_adapter (0.5.0) - paperclip (5.1.0) - activemodel (>= 4.2.0) - activesupport (>= 4.2.0) - cocaine (~> 0.5.5) - mime-types - mimemagic (~> 0.3.0) parallel (1.12.1) paranoia (2.4.0) activerecord (>= 4.0, < 5.2) @@ -402,20 +366,15 @@ GEM rb-fsevent (0.10.2) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) - redcarpet (3.4.0) redis (4.0.1) responders (2.4.0) actionpack (>= 4.2.0, < 5.3) railties (>= 4.2.0, < 5.3) - rest-client (2.0.2) - http-cookie (>= 1.0.2, < 2.0) - mime-types (>= 1.16, < 4.0) - netrc (~> 0.8) rspec-activemodel-mocks (1.0.3) activemodel (>= 3.0) activesupport (>= 3.0) rspec-mocks (>= 2.99, < 4.0) - rspec-core (3.7.0) + rspec-core (3.7.1) rspec-support (~> 3.7.0) rspec-expectations (3.7.0) diff-lcs (>= 1.2.0, < 2.0) @@ -465,7 +424,7 @@ GEM connection_pool (~> 2.2, >= 2.2.0) rack-protection (>= 1.5.0) redis (>= 3.3.4, < 5) - simplecov (0.15.1) + simplecov (0.12.0) docile (~> 1.1.0) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) @@ -485,7 +444,7 @@ GEM tins (~> 1.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - thor (0.20.0) + thor (0.19.4) thread (0.2.2) thread_safe (0.3.6) tilt (2.0.8) @@ -496,9 +455,6 @@ GEM thread_safe (~> 0.1) uglifier (4.1.2) execjs (>= 0.3.0, < 3) - unf (0.1.4) - unf_ext - unf_ext (0.0.7.4) unicode-display_width (1.3.0) unicorn (5.4.0) kgio (~> 2.6) @@ -541,7 +497,6 @@ DEPENDENCIES capybara-screenshot codeclimate-test-reporter coffee-rails - comfortable_mexican_sofa coveralls csv_shaper d3-rails @@ -610,4 +565,4 @@ RUBY VERSION ruby 2.4.1p111 BUNDLED WITH - 1.16.0 + 1.16.1 From 79cf7b452c528360e83ee2b4cbee7b41f89dbe3f Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 4 Jan 2018 14:31:24 +1300 Subject: [PATCH 030/219] removing some cms stuff --- app/views/layouts/_footer.html.haml | 6 +- .../initializers/comfortable_mexican_sofa.rb | 101 ------------------ 2 files changed, 3 insertions(+), 104 deletions(-) delete mode 100644 config/initializers/comfortable_mexican_sofa.rb diff --git a/app/views/layouts/_footer.html.haml b/app/views/layouts/_footer.html.haml index e004bf69a..b05b164d4 100644 --- a/app/views/layouts/_footer.html.haml +++ b/app/views/layouts/_footer.html.haml @@ -2,11 +2,11 @@ .container .row .col-md-4#footer1 - != cms_snippet_content(:footer1) + -# != cms_snippet_content(:footer1) .col-md-4#footer2 - != cms_snippet_content(:footer2) + -# != cms_snippet_content(:footer2) .col-md-4#footer3 - != cms_snippet_content(:footer3) + -# != cms_snippet_content(:footer3) %div{ style: "float: right;" } %a{ href: "http://opendefinition.org/ossd/" } %img{ src: "http://assets.okfn.org/images/ok_buttons/os_80x15_blue.png", alt: "" } diff --git a/config/initializers/comfortable_mexican_sofa.rb b/config/initializers/comfortable_mexican_sofa.rb deleted file mode 100644 index 78794d93c..000000000 --- a/config/initializers/comfortable_mexican_sofa.rb +++ /dev/null @@ -1,101 +0,0 @@ -# encoding: utf-8 - -ComfortableMexicanSofa.configure do |config| - # Title of the admin area - # config.cms_title = 'ComfortableMexicanSofa CMS Engine' - - # Controller that is inherited from CmsAdmin::BaseController - # config.base_controller = 'ApplicationController' - - # Module responsible for authentication. You can replace it with your own. - # It simply needs to have #authenticate method. See http_auth.rb for reference. - config.admin_auth = 'CmsDeviseAuth' - - # Module responsible for authorization on admin side. It should have #authorize - # method that returns true or false based on params and loaded instance - # variables available for a given controller. - # config.admin_authorization = 'ComfyAdminAuthorization' - - # Module responsible for public authentication. Similar to the above. You also - # will have access to @cms_site, @cms_layout, @cms_page so you can use them in - # your logic. Default module doesn't do anything. - # config.public_auth = 'ComfyPublicAuthentication' - - # When arriving at /cms-admin you may chose to redirect to arbirtary path, - # for example '/cms-admin/users' - # config.admin_route_redirect = '' - - # File uploads use Paperclip and can support filesystem or s3 uploads. Override - # the upload method and appropriate settings based on Paperclip. For S3 see: - # http://rdoc.info/gems/paperclip/2.3.8/Paperclip/Storage/S3, and for - # filesystem see: http://rdoc.info/gems/paperclip/2.3.8/Paperclip/Storage/Filesystem - # If you are using S3 and HTTPS, pass :s3_protocol => '' to have URLs that use the protocol of the page - # config.upload_file_options = {:url => '/system/:class/:id/:attachment/:style/:filename'} - - # Sofa allows you to setup entire site from files. Database is updated with each - # request (if necessary). Please note that database entries are destroyed if there's - # no corresponding file. Fixtures are disabled by default. - # config.enable_fixtures = false - - # Path where fixtures can be located. - # config.fixtures_path = File.expand_path('db/cms_fixtures', Rails.root) - - # Importing fixtures into Database - # To load fixtures into the database just run this rake task: - # local: $ rake comfortable_mexican_sofa:fixtures:import FROM=example.local TO=localhost - # Heroku: $ heroku run rake comfortable_mexican_sofa:fixtures:import FROM=example.local TO=yourapp.herokuapp.com - # From indicates folder the fixtures are in and to is the Site hostname you have defined in the database. - - # Exporting fixtures into Files - # If you need to dump database contents into fixture files run: - # local: $ rake comfortable_mexican_sofa:fixtures:export FROM=localhost TO=example.local - # Heroku: $ heroku run rake comfortable_mexican_sofa:fixtures:export FROM=yourapp.herokuapp.com TO=example.local - # This will create example.local folder and dump all content from example.com Site. - - # Content for Layouts, Pages and Snippets has a revision history. You can revert - # a previous version using this system. You can control how many revisions per - # object you want to keep. Set it to 0 if you wish to turn this feature off. - # config.revisions_limit = 25 - - # Locale definitions. If you want to define your own locale merge - # {:locale => 'Locale Title'} with this. - # config.locales = {:en => 'English', :es => 'Español'} - - # Admin interface will respect the locale of the site being managed. However you can - # force it to English by setting this to `:en` - # config.admin_locale = nil - - # A class that is included as a sweeper to admin base controller if it's set - # config.admin_cache_sweeper = nil - - # By default you cannot have irb code inside your layouts/pages/snippets. - # Generally this is to prevent putting something like this: - # <% User.delete_all %> but if you really want to allow it... - # config.allow_irb = false - - # Whitelist of all helper methods that can be used via {{cms:helper}} tag. By default - # all helpers are allowed except `eval`, `send`, `call` and few others. Empty array - # will prevent rendering of all helpers. - # config.allowed_helpers = nil - - # Whitelist of partials paths that can be used via {{cms:partial}} tag. All partials - # are accessible by default. Empty array will prevent rendering of all partials. - # config.allowed_partials = nil - - # Site aliases, if you want to have aliases for your site. Good for harmonizing - # production env with dev/testing envs. - # e.g. config.hostname_aliases = {'host.com' => 'host.inv', 'host_a.com' => ['host.lvh.me', 'host.dev']} - # Default is nil (not used) - # config.hostname_aliases = nil - - # Reveal partials that can be overwritten in the admin area. - # Default is false. - # config.reveal_cms_partials = false -end - -module CmsDeviseAuth - def authenticate - return if current_member && current_member.role?(:admin) - redirect_to root_path, alert: 'Permission denied. Please sign in as an admin user to use the CMS admin area.' - end -end From 2109ee1561667cdb6e894a176c94003fcc7c466e Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 4 Jan 2018 14:34:30 +1300 Subject: [PATCH 031/219] Migrations need to specify a version --- db/migrate/20120903092956_devise_create_users.rb | 2 +- db/migrate/20120903112806_add_username_to_users.rb | 2 +- db/migrate/20121001212604_create_crops.rb | 2 +- .../20121003190731_require_system_name_for_crops.rb | 2 +- db/migrate/20121027035231_add_slug_to_crops.rb | 2 +- db/migrate/20121105032913_create_gardens.rb | 2 +- db/migrate/20121106101718_add_slug_to_users.rb | 2 +- db/migrate/20121107012827_create_scientific_names.rb | 2 +- db/migrate/20121108105440_create_updates.rb | 2 +- .../20121109130033_add_creation_index_to_updates.rb | 2 +- db/migrate/20121203034745_add_tos_agreement_to_users.rb | 2 +- db/migrate/20121214224227_add_slug_to_updates.rb | 2 +- db/migrate/20121219022554_create_plantings.rb | 2 +- db/migrate/20130113045802_rename_updates_to_posts.rb | 2 +- db/migrate/20130113060852_rename_users_to_members.rb | 2 +- .../20130113081521_rename_post_member_to_author.rb | 2 +- .../20130113095802_rename_garden_member_to_owner.rb | 2 +- db/migrate/20130118031942_add_description_to_gardens.rb | 2 +- db/migrate/20130118043431_add_slug_to_plantings.rb | 2 +- db/migrate/20130206033956_create_comments.rb | 2 +- db/migrate/20130206051328_add_show_email_to_member.rb | 2 +- db/migrate/20130208034248_require_fields_for_comments.rb | 2 +- db/migrate/20130212001748_add_geo_to_members.rb | 2 +- db/migrate/20130212123628_create_notifications.rb | 2 +- db/migrate/20130213014511_create_forums.rb | 2 +- db/migrate/20130213015708_add_forum_to_posts.rb | 2 +- db/migrate/20130214024117_create_roles.rb | 2 +- db/migrate/20130214034838_add_members_roles_table.rb | 2 +- db/migrate/20130215131921_rename_notification_fields.rb | 2 +- db/migrate/20130220044605_add_slug_to_forums.rb | 2 +- db/migrate/20130220044642_add_slug_to_roles.rb | 2 +- db/migrate/20130222060730_default_read_to_false.rb | 2 +- db/migrate/20130326092227_change_planted_at_to_date.rb | 2 +- db/migrate/20130327120024_add_send_email_to_member.rb | 2 +- db/migrate/20130329045744_add_sunniness_to_planting.rb | 2 +- db/migrate/20130404174459_create_authentications.rb | 2 +- db/migrate/20130409103549_make_post_subject_non_null.rb | 2 +- db/migrate/20130409162140_add_name_to_authentications.rb | 2 +- db/migrate/20130507105357_create_products.rb | 2 +- db/migrate/20130507110411_create_orders.rb | 2 +- db/migrate/20130507113915_add_orders_products_table.rb | 2 +- db/migrate/20130508050711_add_completed_to_order.rb | 2 +- db/migrate/20130508104506_create_photos.rb | 2 +- db/migrate/20130509123711_add_metadata_to_photos.rb | 2 +- db/migrate/20130514124515_add_parent_to_crop.rb | 2 +- db/migrate/20130515033842_create_order_items.rb | 2 +- .../20130515054017_change_order_member_id_to_integer.rb | 2 +- db/migrate/20130515122301_change_prices_to_integers.rb | 2 +- db/migrate/20130517015920_create_account_details.rb | 2 +- db/migrate/20130517051922_create_account_types.rb | 2 +- db/migrate/20130517234458_require_account_type_name.rb | 2 +- db/migrate/20130518000339_add_columns_to_product.rb | 2 +- .../20130518002942_rename_account_detail_to_account.rb | 2 +- db/migrate/20130529032813_add_express_token_to_orders.rb | 2 +- db/migrate/20130531110729_add_photos_plantings_table.rb | 2 +- .../20130601011725_change_flickr_photo_id_to_string.rb | 2 +- .../20130606230333_change_product_description_to_text.rb | 2 +- .../20130606233733_add_recommended_price_to_product.rb | 2 +- .../20130705104238_add_planted_from_to_planting.rb | 2 +- db/migrate/20130715110134_create_seeds.rb | 2 +- ...130718005600_change_use_by_to_plant_before_on_seed.rb | 2 +- db/migrate/20130718011247_add_trading_to_seeds.rb | 2 +- db/migrate/20130722050836_remove_tradable_from_seeds.rb | 2 +- .../20130723103128_set_default_tradable_to_on_seed.rb | 2 +- db/migrate/20130723110702_add_slug_to_seed.rb | 2 +- db/migrate/20130809012511_add_bio_to_members.rb | 2 +- db/migrate/20130819004549_add_planting_count_to_crop.rb | 2 +- db/migrate/20130821011352_add_creator_to_crops.rb | 2 +- .../20130821073736_add_creator_to_scientific_name.rb | 2 +- db/migrate/20130826012139_add_owner_to_planting.rb | 2 +- .../20130826023159_add_plantings_count_to_member.rb | 2 +- db/migrate/20130827105823_add_newsletter_to_member.rb | 2 +- db/migrate/20130913015118_add_referral_code_to_order.rb | 2 +- db/migrate/20130917053547_create_harvests.rb | 2 +- ...20130917060257_change_harvest_notes_to_description.rb | 2 +- .../20130917071545_change_harvest_units_to_unit.rb | 2 +- db/migrate/20130917075803_add_slug_to_harvests.rb | 2 +- db/migrate/20130925050304_add_weight_to_harvests.rb | 2 +- db/migrate/20131018101204_rename_system_name_to_name.rb | 2 +- db/migrate/20131025104228_add_fields_to_gardens.rb | 2 +- db/migrate/20131029053113_add_plant_part_to_harvests.rb | 2 +- db/migrate/20131030230908_create_plant_parts.rb | 2 +- .../20131030231202_change_plant_part_to_plant_part_id.rb | 2 +- db/migrate/20131031000655_add_slug_to_plant_part.rb | 2 +- .../20140718075753_default_plantings_count_to_zero.rb | 2 +- db/migrate/20140829230600_add_finished_to_planting.rb | 2 +- db/migrate/20140905001730_add_harvests_photos_table.rb | 2 +- db/migrate/20140928044231_add_crops_posts_table.rb | 2 +- ...0140928085713_add_send_planting_reminder_to_member.rb | 2 +- db/migrate/20141002022459_create_index_harvest_photos.rb | 2 +- db/migrate/20141018111015_create_alternate_names.rb | 2 +- db/migrate/20141111130849_create_follows.rb | 2 +- ...1119130555_change_follows_member_id_to_follower_id.rb | 2 +- db/migrate/20150124110540_add_properties_to_seeds.rb | 2 +- db/migrate/20150127043022_add_gardens_photos_table.rb | 2 +- db/migrate/20150129034206_add_si_weight_to_harvest.rb | 2 +- db/migrate/20150130224814_add_requester_to_crops.rb | 2 +- db/migrate/20150201052245_create_cms.rb | 2 +- .../20150201053200_add_approval_status_to_crops.rb | 2 +- .../20150201062506_add_reason_for_rejection_to_crops.rb | 2 +- db/migrate/20150201064502_add_request_notes_to_crops.rb | 2 +- db/migrate/20150203080226_create_likes.rb | 2 +- .../20150209105410_add_rejection_notes_to_crops.rb | 2 +- ...150625224805_add_days_before_maturity_to_plantings.rb | 2 +- db/migrate/20150824145414_add_member_preferred_image.rb | 2 +- db/migrate/20161129021533_rename_scientific_name.rb | 2 +- db/migrate/20161201154922_add_photos_seeds_table.rb | 2 +- .../20170104035248_add_planting_ref_to_harvests.rb | 2 +- db/migrate/20170413221549_counter_caches.rb | 2 +- db/migrate/20170520060252_add_deleted_to_members.rb | 2 +- db/migrate/20171022032108_all_the_predictions.rb | 2 +- db/migrate/20171028230429_create_median_function.rb | 2 +- db/migrate/20171105011017_set_prediction_data.rb | 2 +- db/migrate/20171129041341_create_photographings.rb | 2 +- db/schema.rb | 9 +++------ 115 files changed, 117 insertions(+), 120 deletions(-) diff --git a/db/migrate/20120903092956_devise_create_users.rb b/db/migrate/20120903092956_devise_create_users.rb index cf02cbe2a..62d84f3d1 100644 --- a/db/migrate/20120903092956_devise_create_users.rb +++ b/db/migrate/20120903092956_devise_create_users.rb @@ -1,4 +1,4 @@ -class DeviseCreateUsers < ActiveRecord::Migration +class DeviseCreateUsers < ActiveRecord::Migration[4.2] def change create_table(:users) do |t| ## Database authenticatable diff --git a/db/migrate/20120903112806_add_username_to_users.rb b/db/migrate/20120903112806_add_username_to_users.rb index 3b71a2769..2df3aa569 100644 --- a/db/migrate/20120903112806_add_username_to_users.rb +++ b/db/migrate/20120903112806_add_username_to_users.rb @@ -1,4 +1,4 @@ -class AddUsernameToUsers < ActiveRecord::Migration +class AddUsernameToUsers < ActiveRecord::Migration[4.2] def change add_column :users, :username, :string end diff --git a/db/migrate/20121001212604_create_crops.rb b/db/migrate/20121001212604_create_crops.rb index c0fd9fec5..6e6fbcc2c 100644 --- a/db/migrate/20121001212604_create_crops.rb +++ b/db/migrate/20121001212604_create_crops.rb @@ -1,4 +1,4 @@ -class CreateCrops < ActiveRecord::Migration +class CreateCrops < ActiveRecord::Migration[4.2] def change create_table :crops do |t| t.string :system_name diff --git a/db/migrate/20121003190731_require_system_name_for_crops.rb b/db/migrate/20121003190731_require_system_name_for_crops.rb index f4536c18b..7989fcecf 100644 --- a/db/migrate/20121003190731_require_system_name_for_crops.rb +++ b/db/migrate/20121003190731_require_system_name_for_crops.rb @@ -1,4 +1,4 @@ -class RequireSystemNameForCrops < ActiveRecord::Migration +class RequireSystemNameForCrops < ActiveRecord::Migration[4.2] def up change_table :crops do |t| t.index :system_name diff --git a/db/migrate/20121027035231_add_slug_to_crops.rb b/db/migrate/20121027035231_add_slug_to_crops.rb index f826e1705..d360b4a44 100644 --- a/db/migrate/20121027035231_add_slug_to_crops.rb +++ b/db/migrate/20121027035231_add_slug_to_crops.rb @@ -1,4 +1,4 @@ -class AddSlugToCrops < ActiveRecord::Migration +class AddSlugToCrops < ActiveRecord::Migration[4.2] def change add_column :crops, :slug, :string add_index :crops, :slug, unique: true diff --git a/db/migrate/20121105032913_create_gardens.rb b/db/migrate/20121105032913_create_gardens.rb index a269a1e5a..e6c7834e1 100644 --- a/db/migrate/20121105032913_create_gardens.rb +++ b/db/migrate/20121105032913_create_gardens.rb @@ -1,4 +1,4 @@ -class CreateGardens < ActiveRecord::Migration +class CreateGardens < ActiveRecord::Migration[4.2] def change create_table :gardens do |t| t.string :name, null: false diff --git a/db/migrate/20121106101718_add_slug_to_users.rb b/db/migrate/20121106101718_add_slug_to_users.rb index a551cf15f..5840e09d2 100644 --- a/db/migrate/20121106101718_add_slug_to_users.rb +++ b/db/migrate/20121106101718_add_slug_to_users.rb @@ -1,4 +1,4 @@ -class AddSlugToUsers < ActiveRecord::Migration +class AddSlugToUsers < ActiveRecord::Migration[4.2] def change add_column :users, :slug, :string add_index :users, :slug, unique: true diff --git a/db/migrate/20121107012827_create_scientific_names.rb b/db/migrate/20121107012827_create_scientific_names.rb index 4300e3667..0e6b179f7 100644 --- a/db/migrate/20121107012827_create_scientific_names.rb +++ b/db/migrate/20121107012827_create_scientific_names.rb @@ -1,4 +1,4 @@ -class CreateScientificNames < ActiveRecord::Migration +class CreateScientificNames < ActiveRecord::Migration[4.2] def change create_table :scientific_names do |t| t.string :scientific_name, null: false diff --git a/db/migrate/20121108105440_create_updates.rb b/db/migrate/20121108105440_create_updates.rb index 6db35c046..3caacb749 100644 --- a/db/migrate/20121108105440_create_updates.rb +++ b/db/migrate/20121108105440_create_updates.rb @@ -1,4 +1,4 @@ -class CreateUpdates < ActiveRecord::Migration +class CreateUpdates < ActiveRecord::Migration[4.2] def change create_table :updates do |t| t.integer :user_id, null: false diff --git a/db/migrate/20121109130033_add_creation_index_to_updates.rb b/db/migrate/20121109130033_add_creation_index_to_updates.rb index 46601ef3d..57c24d559 100644 --- a/db/migrate/20121109130033_add_creation_index_to_updates.rb +++ b/db/migrate/20121109130033_add_creation_index_to_updates.rb @@ -1,4 +1,4 @@ -class AddCreationIndexToUpdates < ActiveRecord::Migration +class AddCreationIndexToUpdates < ActiveRecord::Migration[4.2] def change add_index :updates, %i(created_at user_id) end diff --git a/db/migrate/20121203034745_add_tos_agreement_to_users.rb b/db/migrate/20121203034745_add_tos_agreement_to_users.rb index aacc06970..31354ea58 100644 --- a/db/migrate/20121203034745_add_tos_agreement_to_users.rb +++ b/db/migrate/20121203034745_add_tos_agreement_to_users.rb @@ -1,4 +1,4 @@ -class AddTosAgreementToUsers < ActiveRecord::Migration +class AddTosAgreementToUsers < ActiveRecord::Migration[4.2] def change add_column :users, :tos_agreement, :boolean end diff --git a/db/migrate/20121214224227_add_slug_to_updates.rb b/db/migrate/20121214224227_add_slug_to_updates.rb index 85a0510d5..78d25ef24 100644 --- a/db/migrate/20121214224227_add_slug_to_updates.rb +++ b/db/migrate/20121214224227_add_slug_to_updates.rb @@ -1,4 +1,4 @@ -class AddSlugToUpdates < ActiveRecord::Migration +class AddSlugToUpdates < ActiveRecord::Migration[4.2] def change add_column :updates, :slug, :string add_index :updates, :slug, unique: true diff --git a/db/migrate/20121219022554_create_plantings.rb b/db/migrate/20121219022554_create_plantings.rb index 67ffa962d..d586e2426 100644 --- a/db/migrate/20121219022554_create_plantings.rb +++ b/db/migrate/20121219022554_create_plantings.rb @@ -1,4 +1,4 @@ -class CreatePlantings < ActiveRecord::Migration +class CreatePlantings < ActiveRecord::Migration[4.2] def change create_table :plantings do |t| t.integer :garden_id, null: false diff --git a/db/migrate/20130113045802_rename_updates_to_posts.rb b/db/migrate/20130113045802_rename_updates_to_posts.rb index 9fa730976..6329df2bb 100644 --- a/db/migrate/20130113045802_rename_updates_to_posts.rb +++ b/db/migrate/20130113045802_rename_updates_to_posts.rb @@ -1,4 +1,4 @@ -class RenameUpdatesToPosts < ActiveRecord::Migration +class RenameUpdatesToPosts < ActiveRecord::Migration[4.2] def change rename_table :updates, :posts end diff --git a/db/migrate/20130113060852_rename_users_to_members.rb b/db/migrate/20130113060852_rename_users_to_members.rb index e01458fe2..bb248186e 100644 --- a/db/migrate/20130113060852_rename_users_to_members.rb +++ b/db/migrate/20130113060852_rename_users_to_members.rb @@ -1,4 +1,4 @@ -class RenameUsersToMembers < ActiveRecord::Migration +class RenameUsersToMembers < ActiveRecord::Migration[4.2] def change rename_table :users, :members rename_column :members, :username, :login_name diff --git a/db/migrate/20130113081521_rename_post_member_to_author.rb b/db/migrate/20130113081521_rename_post_member_to_author.rb index 8bfea089b..a1658925b 100644 --- a/db/migrate/20130113081521_rename_post_member_to_author.rb +++ b/db/migrate/20130113081521_rename_post_member_to_author.rb @@ -1,4 +1,4 @@ -class RenamePostMemberToAuthor < ActiveRecord::Migration +class RenamePostMemberToAuthor < ActiveRecord::Migration[4.2] def change rename_column :posts, :member_id, :author_id end diff --git a/db/migrate/20130113095802_rename_garden_member_to_owner.rb b/db/migrate/20130113095802_rename_garden_member_to_owner.rb index e6dbb0df5..ea6bd4c72 100644 --- a/db/migrate/20130113095802_rename_garden_member_to_owner.rb +++ b/db/migrate/20130113095802_rename_garden_member_to_owner.rb @@ -1,4 +1,4 @@ -class RenameGardenMemberToOwner < ActiveRecord::Migration +class RenameGardenMemberToOwner < ActiveRecord::Migration[4.2] def change rename_column :gardens, :member_id, :owner_id end diff --git a/db/migrate/20130118031942_add_description_to_gardens.rb b/db/migrate/20130118031942_add_description_to_gardens.rb index 30bb33c7e..f8eb0a441 100644 --- a/db/migrate/20130118031942_add_description_to_gardens.rb +++ b/db/migrate/20130118031942_add_description_to_gardens.rb @@ -1,4 +1,4 @@ -class AddDescriptionToGardens < ActiveRecord::Migration +class AddDescriptionToGardens < ActiveRecord::Migration[4.2] def change add_column :gardens, :description, :text end diff --git a/db/migrate/20130118043431_add_slug_to_plantings.rb b/db/migrate/20130118043431_add_slug_to_plantings.rb index e3e9fa46d..c4e5d434d 100644 --- a/db/migrate/20130118043431_add_slug_to_plantings.rb +++ b/db/migrate/20130118043431_add_slug_to_plantings.rb @@ -1,4 +1,4 @@ -class AddSlugToPlantings < ActiveRecord::Migration +class AddSlugToPlantings < ActiveRecord::Migration[4.2] def change add_column :plantings, :slug, :string add_index :plantings, :slug, unique: true diff --git a/db/migrate/20130206033956_create_comments.rb b/db/migrate/20130206033956_create_comments.rb index 79e42c688..0c4663ec7 100644 --- a/db/migrate/20130206033956_create_comments.rb +++ b/db/migrate/20130206033956_create_comments.rb @@ -1,4 +1,4 @@ -class CreateComments < ActiveRecord::Migration +class CreateComments < ActiveRecord::Migration[4.2] def change create_table :comments do |t| t.integer :post_id diff --git a/db/migrate/20130206051328_add_show_email_to_member.rb b/db/migrate/20130206051328_add_show_email_to_member.rb index b18d3ee3d..3185b1377 100644 --- a/db/migrate/20130206051328_add_show_email_to_member.rb +++ b/db/migrate/20130206051328_add_show_email_to_member.rb @@ -1,4 +1,4 @@ -class AddShowEmailToMember < ActiveRecord::Migration +class AddShowEmailToMember < ActiveRecord::Migration[4.2] def change add_column :members, :show_email, :boolean end diff --git a/db/migrate/20130208034248_require_fields_for_comments.rb b/db/migrate/20130208034248_require_fields_for_comments.rb index ecb17f302..da847671b 100644 --- a/db/migrate/20130208034248_require_fields_for_comments.rb +++ b/db/migrate/20130208034248_require_fields_for_comments.rb @@ -1,4 +1,4 @@ -class RequireFieldsForComments < ActiveRecord::Migration +class RequireFieldsForComments < ActiveRecord::Migration[4.2] def up change_table :comments do |t| t.change :post_id, :integer, null: false diff --git a/db/migrate/20130212001748_add_geo_to_members.rb b/db/migrate/20130212001748_add_geo_to_members.rb index f14173795..e60f355f8 100644 --- a/db/migrate/20130212001748_add_geo_to_members.rb +++ b/db/migrate/20130212001748_add_geo_to_members.rb @@ -1,4 +1,4 @@ -class AddGeoToMembers < ActiveRecord::Migration +class AddGeoToMembers < ActiveRecord::Migration[4.2] def change add_column :members, :location, :string add_column :members, :latitude, :float diff --git a/db/migrate/20130212123628_create_notifications.rb b/db/migrate/20130212123628_create_notifications.rb index 6a59cdd8f..b89b13656 100644 --- a/db/migrate/20130212123628_create_notifications.rb +++ b/db/migrate/20130212123628_create_notifications.rb @@ -1,4 +1,4 @@ -class CreateNotifications < ActiveRecord::Migration +class CreateNotifications < ActiveRecord::Migration[4.2] def change create_table :notifications do |t| t.integer :from_id diff --git a/db/migrate/20130213014511_create_forums.rb b/db/migrate/20130213014511_create_forums.rb index 2d5b34694..0df3f00a2 100644 --- a/db/migrate/20130213014511_create_forums.rb +++ b/db/migrate/20130213014511_create_forums.rb @@ -1,4 +1,4 @@ -class CreateForums < ActiveRecord::Migration +class CreateForums < ActiveRecord::Migration[4.2] def change create_table :forums do |t| t.string :name, null: false diff --git a/db/migrate/20130213015708_add_forum_to_posts.rb b/db/migrate/20130213015708_add_forum_to_posts.rb index 904183f90..315ae0be5 100644 --- a/db/migrate/20130213015708_add_forum_to_posts.rb +++ b/db/migrate/20130213015708_add_forum_to_posts.rb @@ -1,4 +1,4 @@ -class AddForumToPosts < ActiveRecord::Migration +class AddForumToPosts < ActiveRecord::Migration[4.2] def change add_column :posts, :forum_id, :integer end diff --git a/db/migrate/20130214024117_create_roles.rb b/db/migrate/20130214024117_create_roles.rb index a95150da4..e39c7565d 100644 --- a/db/migrate/20130214024117_create_roles.rb +++ b/db/migrate/20130214024117_create_roles.rb @@ -1,4 +1,4 @@ -class CreateRoles < ActiveRecord::Migration +class CreateRoles < ActiveRecord::Migration[4.2] def change create_table :roles do |t| t.string :name, null: false diff --git a/db/migrate/20130214034838_add_members_roles_table.rb b/db/migrate/20130214034838_add_members_roles_table.rb index 9e84fd37d..502266a87 100644 --- a/db/migrate/20130214034838_add_members_roles_table.rb +++ b/db/migrate/20130214034838_add_members_roles_table.rb @@ -1,4 +1,4 @@ -class AddMembersRolesTable < ActiveRecord::Migration +class AddMembersRolesTable < ActiveRecord::Migration[4.2] def change create_table :members_roles, id: false do |t| t.integer :member_id diff --git a/db/migrate/20130215131921_rename_notification_fields.rb b/db/migrate/20130215131921_rename_notification_fields.rb index 63f52ae6d..c2294d2fe 100644 --- a/db/migrate/20130215131921_rename_notification_fields.rb +++ b/db/migrate/20130215131921_rename_notification_fields.rb @@ -1,4 +1,4 @@ -class RenameNotificationFields < ActiveRecord::Migration +class RenameNotificationFields < ActiveRecord::Migration[4.2] def change change_table :notifications do |t| t.rename :to_id, :recipient_id diff --git a/db/migrate/20130220044605_add_slug_to_forums.rb b/db/migrate/20130220044605_add_slug_to_forums.rb index 6b7db208f..57fbdc145 100644 --- a/db/migrate/20130220044605_add_slug_to_forums.rb +++ b/db/migrate/20130220044605_add_slug_to_forums.rb @@ -1,4 +1,4 @@ -class AddSlugToForums < ActiveRecord::Migration +class AddSlugToForums < ActiveRecord::Migration[4.2] def change add_column :forums, :slug, :string add_index :forums, :slug, unique: true diff --git a/db/migrate/20130220044642_add_slug_to_roles.rb b/db/migrate/20130220044642_add_slug_to_roles.rb index bc4578b10..a3812d77b 100644 --- a/db/migrate/20130220044642_add_slug_to_roles.rb +++ b/db/migrate/20130220044642_add_slug_to_roles.rb @@ -1,4 +1,4 @@ -class AddSlugToRoles < ActiveRecord::Migration +class AddSlugToRoles < ActiveRecord::Migration[4.2] def change add_column :roles, :slug, :string add_index :roles, :slug, unique: true diff --git a/db/migrate/20130222060730_default_read_to_false.rb b/db/migrate/20130222060730_default_read_to_false.rb index efe6d4b61..81a10aa40 100644 --- a/db/migrate/20130222060730_default_read_to_false.rb +++ b/db/migrate/20130222060730_default_read_to_false.rb @@ -1,4 +1,4 @@ -class DefaultReadToFalse < ActiveRecord::Migration +class DefaultReadToFalse < ActiveRecord::Migration[4.2] def up change_table :notifications do |t| t.change :read, :boolean, default: false diff --git a/db/migrate/20130326092227_change_planted_at_to_date.rb b/db/migrate/20130326092227_change_planted_at_to_date.rb index bf14c624d..089c77ef5 100644 --- a/db/migrate/20130326092227_change_planted_at_to_date.rb +++ b/db/migrate/20130326092227_change_planted_at_to_date.rb @@ -1,4 +1,4 @@ -class ChangePlantedAtToDate < ActiveRecord::Migration +class ChangePlantedAtToDate < ActiveRecord::Migration[4.2] def change change_column :plantings, :planted_at, :date end diff --git a/db/migrate/20130327120024_add_send_email_to_member.rb b/db/migrate/20130327120024_add_send_email_to_member.rb index b83dd9e03..5421bb0d7 100644 --- a/db/migrate/20130327120024_add_send_email_to_member.rb +++ b/db/migrate/20130327120024_add_send_email_to_member.rb @@ -1,4 +1,4 @@ -class AddSendEmailToMember < ActiveRecord::Migration +class AddSendEmailToMember < ActiveRecord::Migration[4.2] def change add_column :members, :send_notification_email, :boolean, default: true end diff --git a/db/migrate/20130329045744_add_sunniness_to_planting.rb b/db/migrate/20130329045744_add_sunniness_to_planting.rb index 8fae8d1d0..74fe7a652 100644 --- a/db/migrate/20130329045744_add_sunniness_to_planting.rb +++ b/db/migrate/20130329045744_add_sunniness_to_planting.rb @@ -1,4 +1,4 @@ -class AddSunninessToPlanting < ActiveRecord::Migration +class AddSunninessToPlanting < ActiveRecord::Migration[4.2] def change add_column :plantings, :sunniness, :string end diff --git a/db/migrate/20130404174459_create_authentications.rb b/db/migrate/20130404174459_create_authentications.rb index 929ac756a..226f1c34c 100644 --- a/db/migrate/20130404174459_create_authentications.rb +++ b/db/migrate/20130404174459_create_authentications.rb @@ -1,4 +1,4 @@ -class CreateAuthentications < ActiveRecord::Migration +class CreateAuthentications < ActiveRecord::Migration[4.2] def change create_table :authentications do |t| t.integer :member_id, null: false diff --git a/db/migrate/20130409103549_make_post_subject_non_null.rb b/db/migrate/20130409103549_make_post_subject_non_null.rb index 877bd048f..ac05aca24 100644 --- a/db/migrate/20130409103549_make_post_subject_non_null.rb +++ b/db/migrate/20130409103549_make_post_subject_non_null.rb @@ -1,3 +1,3 @@ -class MakePostSubjectNonNull < ActiveRecord::Migration +class MakePostSubjectNonNull < ActiveRecord::Migration[4.2] change_column :posts, :subject, :string, null: false end diff --git a/db/migrate/20130409162140_add_name_to_authentications.rb b/db/migrate/20130409162140_add_name_to_authentications.rb index c8e981b5f..dabcb623a 100644 --- a/db/migrate/20130409162140_add_name_to_authentications.rb +++ b/db/migrate/20130409162140_add_name_to_authentications.rb @@ -1,4 +1,4 @@ -class AddNameToAuthentications < ActiveRecord::Migration +class AddNameToAuthentications < ActiveRecord::Migration[4.2] def change add_column :authentications, :name, :string end diff --git a/db/migrate/20130507105357_create_products.rb b/db/migrate/20130507105357_create_products.rb index 66a43c43b..5a1ddd995 100644 --- a/db/migrate/20130507105357_create_products.rb +++ b/db/migrate/20130507105357_create_products.rb @@ -1,4 +1,4 @@ -class CreateProducts < ActiveRecord::Migration +class CreateProducts < ActiveRecord::Migration[4.2] def change create_table :products do |t| t.string :name, null: false diff --git a/db/migrate/20130507110411_create_orders.rb b/db/migrate/20130507110411_create_orders.rb index 8d709e172..7974f10e9 100644 --- a/db/migrate/20130507110411_create_orders.rb +++ b/db/migrate/20130507110411_create_orders.rb @@ -1,4 +1,4 @@ -class CreateOrders < ActiveRecord::Migration +class CreateOrders < ActiveRecord::Migration[4.2] def change create_table :orders do |t| t.string :member_id, null: false diff --git a/db/migrate/20130507113915_add_orders_products_table.rb b/db/migrate/20130507113915_add_orders_products_table.rb index 8666e01ab..3d5845081 100644 --- a/db/migrate/20130507113915_add_orders_products_table.rb +++ b/db/migrate/20130507113915_add_orders_products_table.rb @@ -1,4 +1,4 @@ -class AddOrdersProductsTable < ActiveRecord::Migration +class AddOrdersProductsTable < ActiveRecord::Migration[4.2] def change create_table :orders_products, id: false do |t| t.integer :order_id diff --git a/db/migrate/20130508050711_add_completed_to_order.rb b/db/migrate/20130508050711_add_completed_to_order.rb index bcaffebbf..e7c9f422c 100644 --- a/db/migrate/20130508050711_add_completed_to_order.rb +++ b/db/migrate/20130508050711_add_completed_to_order.rb @@ -1,4 +1,4 @@ -class AddCompletedToOrder < ActiveRecord::Migration +class AddCompletedToOrder < ActiveRecord::Migration[4.2] def change add_column :orders, :completed_at, :datetime end diff --git a/db/migrate/20130508104506_create_photos.rb b/db/migrate/20130508104506_create_photos.rb index 84b54ed6c..b6fb3d19f 100644 --- a/db/migrate/20130508104506_create_photos.rb +++ b/db/migrate/20130508104506_create_photos.rb @@ -1,4 +1,4 @@ -class CreatePhotos < ActiveRecord::Migration +class CreatePhotos < ActiveRecord::Migration[4.2] def change create_table :photos do |t| t.integer :owner_id, null: false diff --git a/db/migrate/20130509123711_add_metadata_to_photos.rb b/db/migrate/20130509123711_add_metadata_to_photos.rb index cb76a901a..e05c260cb 100644 --- a/db/migrate/20130509123711_add_metadata_to_photos.rb +++ b/db/migrate/20130509123711_add_metadata_to_photos.rb @@ -1,4 +1,4 @@ -class AddMetadataToPhotos < ActiveRecord::Migration +class AddMetadataToPhotos < ActiveRecord::Migration[4.2] def up change_table :photos do |t| t.string :title diff --git a/db/migrate/20130514124515_add_parent_to_crop.rb b/db/migrate/20130514124515_add_parent_to_crop.rb index 8a6019808..65427c1c8 100644 --- a/db/migrate/20130514124515_add_parent_to_crop.rb +++ b/db/migrate/20130514124515_add_parent_to_crop.rb @@ -1,4 +1,4 @@ -class AddParentToCrop < ActiveRecord::Migration +class AddParentToCrop < ActiveRecord::Migration[4.2] def change add_column :crops, :parent_id, :integer end diff --git a/db/migrate/20130515033842_create_order_items.rb b/db/migrate/20130515033842_create_order_items.rb index cf06827ca..ed7966f46 100644 --- a/db/migrate/20130515033842_create_order_items.rb +++ b/db/migrate/20130515033842_create_order_items.rb @@ -1,4 +1,4 @@ -class CreateOrderItems < ActiveRecord::Migration +class CreateOrderItems < ActiveRecord::Migration[4.2] def change create_table :order_items do |t| t.integer :order_id diff --git a/db/migrate/20130515054017_change_order_member_id_to_integer.rb b/db/migrate/20130515054017_change_order_member_id_to_integer.rb index 2905204d7..508fb13ee 100644 --- a/db/migrate/20130515054017_change_order_member_id_to_integer.rb +++ b/db/migrate/20130515054017_change_order_member_id_to_integer.rb @@ -1,4 +1,4 @@ -class ChangeOrderMemberIdToInteger < ActiveRecord::Migration +class ChangeOrderMemberIdToInteger < ActiveRecord::Migration[4.2] def up remove_column :orders, :member_id add_column :orders, :member_id, :integer diff --git a/db/migrate/20130515122301_change_prices_to_integers.rb b/db/migrate/20130515122301_change_prices_to_integers.rb index f07d45dea..ef84683b2 100644 --- a/db/migrate/20130515122301_change_prices_to_integers.rb +++ b/db/migrate/20130515122301_change_prices_to_integers.rb @@ -1,4 +1,4 @@ -class ChangePricesToIntegers < ActiveRecord::Migration +class ChangePricesToIntegers < ActiveRecord::Migration[4.2] def up change_column :order_items, :price, :integer change_column :products, :min_price, :integer diff --git a/db/migrate/20130517015920_create_account_details.rb b/db/migrate/20130517015920_create_account_details.rb index 81d94a324..c949798dc 100644 --- a/db/migrate/20130517015920_create_account_details.rb +++ b/db/migrate/20130517015920_create_account_details.rb @@ -1,4 +1,4 @@ -class CreateAccountDetails < ActiveRecord::Migration +class CreateAccountDetails < ActiveRecord::Migration[4.2] def change create_table :account_details do |t| t.integer :member_id, null: false diff --git a/db/migrate/20130517051922_create_account_types.rb b/db/migrate/20130517051922_create_account_types.rb index 8858e9cad..25bef37b0 100644 --- a/db/migrate/20130517051922_create_account_types.rb +++ b/db/migrate/20130517051922_create_account_types.rb @@ -1,4 +1,4 @@ -class CreateAccountTypes < ActiveRecord::Migration +class CreateAccountTypes < ActiveRecord::Migration[4.2] def change create_table :account_types do |t| t.string :name diff --git a/db/migrate/20130517234458_require_account_type_name.rb b/db/migrate/20130517234458_require_account_type_name.rb index 6e745cb4b..2b118492a 100644 --- a/db/migrate/20130517234458_require_account_type_name.rb +++ b/db/migrate/20130517234458_require_account_type_name.rb @@ -1,4 +1,4 @@ -class RequireAccountTypeName < ActiveRecord::Migration +class RequireAccountTypeName < ActiveRecord::Migration[4.2] def up change_column :account_types, :name, :string, null: false end diff --git a/db/migrate/20130518000339_add_columns_to_product.rb b/db/migrate/20130518000339_add_columns_to_product.rb index bb3bcf781..b3bba3023 100644 --- a/db/migrate/20130518000339_add_columns_to_product.rb +++ b/db/migrate/20130518000339_add_columns_to_product.rb @@ -1,4 +1,4 @@ -class AddColumnsToProduct < ActiveRecord::Migration +class AddColumnsToProduct < ActiveRecord::Migration[4.2] def change add_column :products, :account_type_id, :integer add_column :products, :paid_months, :integer diff --git a/db/migrate/20130518002942_rename_account_detail_to_account.rb b/db/migrate/20130518002942_rename_account_detail_to_account.rb index 0a8434694..a806c63a7 100644 --- a/db/migrate/20130518002942_rename_account_detail_to_account.rb +++ b/db/migrate/20130518002942_rename_account_detail_to_account.rb @@ -1,4 +1,4 @@ -class RenameAccountDetailToAccount < ActiveRecord::Migration +class RenameAccountDetailToAccount < ActiveRecord::Migration[4.2] def change rename_table :account_details, :accounts end diff --git a/db/migrate/20130529032813_add_express_token_to_orders.rb b/db/migrate/20130529032813_add_express_token_to_orders.rb index cf32be434..e7d40f417 100644 --- a/db/migrate/20130529032813_add_express_token_to_orders.rb +++ b/db/migrate/20130529032813_add_express_token_to_orders.rb @@ -1,4 +1,4 @@ -class AddExpressTokenToOrders < ActiveRecord::Migration +class AddExpressTokenToOrders < ActiveRecord::Migration[4.2] def change add_column :orders, :paypal_express_token, :string add_column :orders, :paypal_express_payer_id, :string diff --git a/db/migrate/20130531110729_add_photos_plantings_table.rb b/db/migrate/20130531110729_add_photos_plantings_table.rb index ab08ff0d1..f4a3bb7bd 100644 --- a/db/migrate/20130531110729_add_photos_plantings_table.rb +++ b/db/migrate/20130531110729_add_photos_plantings_table.rb @@ -1,4 +1,4 @@ -class AddPhotosPlantingsTable < ActiveRecord::Migration +class AddPhotosPlantingsTable < ActiveRecord::Migration[4.2] def change create_table :photos_plantings, id: false do |t| t.integer :photo_id diff --git a/db/migrate/20130601011725_change_flickr_photo_id_to_string.rb b/db/migrate/20130601011725_change_flickr_photo_id_to_string.rb index 01afc35d3..92cfa1683 100644 --- a/db/migrate/20130601011725_change_flickr_photo_id_to_string.rb +++ b/db/migrate/20130601011725_change_flickr_photo_id_to_string.rb @@ -1,4 +1,4 @@ -class ChangeFlickrPhotoIdToString < ActiveRecord::Migration +class ChangeFlickrPhotoIdToString < ActiveRecord::Migration[4.2] def up remove_column :photos, :flickr_photo_id add_column :photos, :flickr_photo_id, :string diff --git a/db/migrate/20130606230333_change_product_description_to_text.rb b/db/migrate/20130606230333_change_product_description_to_text.rb index d20e44f64..3393a8ae9 100644 --- a/db/migrate/20130606230333_change_product_description_to_text.rb +++ b/db/migrate/20130606230333_change_product_description_to_text.rb @@ -1,4 +1,4 @@ -class ChangeProductDescriptionToText < ActiveRecord::Migration +class ChangeProductDescriptionToText < ActiveRecord::Migration[4.2] def up change_column :products, :description, :text end diff --git a/db/migrate/20130606233733_add_recommended_price_to_product.rb b/db/migrate/20130606233733_add_recommended_price_to_product.rb index c1aff96d4..d424618f5 100644 --- a/db/migrate/20130606233733_add_recommended_price_to_product.rb +++ b/db/migrate/20130606233733_add_recommended_price_to_product.rb @@ -1,4 +1,4 @@ -class AddRecommendedPriceToProduct < ActiveRecord::Migration +class AddRecommendedPriceToProduct < ActiveRecord::Migration[4.2] def change add_column :products, :recommended_price, :integer end diff --git a/db/migrate/20130705104238_add_planted_from_to_planting.rb b/db/migrate/20130705104238_add_planted_from_to_planting.rb index 7eb362a85..1dd4e070d 100644 --- a/db/migrate/20130705104238_add_planted_from_to_planting.rb +++ b/db/migrate/20130705104238_add_planted_from_to_planting.rb @@ -1,4 +1,4 @@ -class AddPlantedFromToPlanting < ActiveRecord::Migration +class AddPlantedFromToPlanting < ActiveRecord::Migration[4.2] def change add_column :plantings, :planted_from, :string end diff --git a/db/migrate/20130715110134_create_seeds.rb b/db/migrate/20130715110134_create_seeds.rb index 6d7cc9674..da8955718 100644 --- a/db/migrate/20130715110134_create_seeds.rb +++ b/db/migrate/20130715110134_create_seeds.rb @@ -1,4 +1,4 @@ -class CreateSeeds < ActiveRecord::Migration +class CreateSeeds < ActiveRecord::Migration[4.2] def change create_table :seeds do |t| t.integer :owner_id, null: false diff --git a/db/migrate/20130718005600_change_use_by_to_plant_before_on_seed.rb b/db/migrate/20130718005600_change_use_by_to_plant_before_on_seed.rb index 23ffefaf9..b440c2704 100644 --- a/db/migrate/20130718005600_change_use_by_to_plant_before_on_seed.rb +++ b/db/migrate/20130718005600_change_use_by_to_plant_before_on_seed.rb @@ -1,4 +1,4 @@ -class ChangeUseByToPlantBeforeOnSeed < ActiveRecord::Migration +class ChangeUseByToPlantBeforeOnSeed < ActiveRecord::Migration[4.2] def change rename_column :seeds, :use_by, :plant_before end diff --git a/db/migrate/20130718011247_add_trading_to_seeds.rb b/db/migrate/20130718011247_add_trading_to_seeds.rb index 2ed459577..52add7f50 100644 --- a/db/migrate/20130718011247_add_trading_to_seeds.rb +++ b/db/migrate/20130718011247_add_trading_to_seeds.rb @@ -1,4 +1,4 @@ -class AddTradingToSeeds < ActiveRecord::Migration +class AddTradingToSeeds < ActiveRecord::Migration[4.2] def change add_column :seeds, :tradable, :boolean add_column :seeds, :tradable_to, :string diff --git a/db/migrate/20130722050836_remove_tradable_from_seeds.rb b/db/migrate/20130722050836_remove_tradable_from_seeds.rb index 7e0457c10..9bea0bd10 100644 --- a/db/migrate/20130722050836_remove_tradable_from_seeds.rb +++ b/db/migrate/20130722050836_remove_tradable_from_seeds.rb @@ -1,4 +1,4 @@ -class RemoveTradableFromSeeds < ActiveRecord::Migration +class RemoveTradableFromSeeds < ActiveRecord::Migration[4.2] def up remove_column :seeds, :tradable end diff --git a/db/migrate/20130723103128_set_default_tradable_to_on_seed.rb b/db/migrate/20130723103128_set_default_tradable_to_on_seed.rb index df94b3150..56b88aadb 100644 --- a/db/migrate/20130723103128_set_default_tradable_to_on_seed.rb +++ b/db/migrate/20130723103128_set_default_tradable_to_on_seed.rb @@ -1,4 +1,4 @@ -class SetDefaultTradableToOnSeed < ActiveRecord::Migration +class SetDefaultTradableToOnSeed < ActiveRecord::Migration[4.2] def up change_column_default(:seeds, :tradable_to, 'nowhere') end diff --git a/db/migrate/20130723110702_add_slug_to_seed.rb b/db/migrate/20130723110702_add_slug_to_seed.rb index 684c2183e..ec52bdad6 100644 --- a/db/migrate/20130723110702_add_slug_to_seed.rb +++ b/db/migrate/20130723110702_add_slug_to_seed.rb @@ -1,4 +1,4 @@ -class AddSlugToSeed < ActiveRecord::Migration +class AddSlugToSeed < ActiveRecord::Migration[4.2] def change add_column :seeds, :slug, :string add_index :seeds, :slug, unique: true diff --git a/db/migrate/20130809012511_add_bio_to_members.rb b/db/migrate/20130809012511_add_bio_to_members.rb index 62a5a7706..b63d11bb4 100644 --- a/db/migrate/20130809012511_add_bio_to_members.rb +++ b/db/migrate/20130809012511_add_bio_to_members.rb @@ -1,4 +1,4 @@ -class AddBioToMembers < ActiveRecord::Migration +class AddBioToMembers < ActiveRecord::Migration[4.2] def change add_column :members, :bio, :text end diff --git a/db/migrate/20130819004549_add_planting_count_to_crop.rb b/db/migrate/20130819004549_add_planting_count_to_crop.rb index 0ae01f4bb..b25fc4a6b 100644 --- a/db/migrate/20130819004549_add_planting_count_to_crop.rb +++ b/db/migrate/20130819004549_add_planting_count_to_crop.rb @@ -1,4 +1,4 @@ -class AddPlantingCountToCrop < ActiveRecord::Migration +class AddPlantingCountToCrop < ActiveRecord::Migration[4.2] def change add_column :crops, :plantings_count, :integer end diff --git a/db/migrate/20130821011352_add_creator_to_crops.rb b/db/migrate/20130821011352_add_creator_to_crops.rb index 4da89b8d1..24aab3f74 100644 --- a/db/migrate/20130821011352_add_creator_to_crops.rb +++ b/db/migrate/20130821011352_add_creator_to_crops.rb @@ -1,4 +1,4 @@ -class AddCreatorToCrops < ActiveRecord::Migration +class AddCreatorToCrops < ActiveRecord::Migration[4.2] def change add_column :crops, :creator_id, :integer end diff --git a/db/migrate/20130821073736_add_creator_to_scientific_name.rb b/db/migrate/20130821073736_add_creator_to_scientific_name.rb index 0905a5c40..e2904811f 100644 --- a/db/migrate/20130821073736_add_creator_to_scientific_name.rb +++ b/db/migrate/20130821073736_add_creator_to_scientific_name.rb @@ -1,4 +1,4 @@ -class AddCreatorToScientificName < ActiveRecord::Migration +class AddCreatorToScientificName < ActiveRecord::Migration[4.2] def change add_column :scientific_names, :creator_id, :integer end diff --git a/db/migrate/20130826012139_add_owner_to_planting.rb b/db/migrate/20130826012139_add_owner_to_planting.rb index 0f32568b2..3399870d9 100644 --- a/db/migrate/20130826012139_add_owner_to_planting.rb +++ b/db/migrate/20130826012139_add_owner_to_planting.rb @@ -1,4 +1,4 @@ -class AddOwnerToPlanting < ActiveRecord::Migration +class AddOwnerToPlanting < ActiveRecord::Migration[4.2] def change add_column :plantings, :owner_id, :integer end diff --git a/db/migrate/20130826023159_add_plantings_count_to_member.rb b/db/migrate/20130826023159_add_plantings_count_to_member.rb index 06819f61c..973479981 100644 --- a/db/migrate/20130826023159_add_plantings_count_to_member.rb +++ b/db/migrate/20130826023159_add_plantings_count_to_member.rb @@ -1,4 +1,4 @@ -class AddPlantingsCountToMember < ActiveRecord::Migration +class AddPlantingsCountToMember < ActiveRecord::Migration[4.2] def change add_column :members, :plantings_count, :integer end diff --git a/db/migrate/20130827105823_add_newsletter_to_member.rb b/db/migrate/20130827105823_add_newsletter_to_member.rb index 919e5222f..25f16a69f 100644 --- a/db/migrate/20130827105823_add_newsletter_to_member.rb +++ b/db/migrate/20130827105823_add_newsletter_to_member.rb @@ -1,4 +1,4 @@ -class AddNewsletterToMember < ActiveRecord::Migration +class AddNewsletterToMember < ActiveRecord::Migration[4.2] def change add_column :members, :newsletter, :boolean end diff --git a/db/migrate/20130913015118_add_referral_code_to_order.rb b/db/migrate/20130913015118_add_referral_code_to_order.rb index efd06c21e..ff9e5a6ff 100644 --- a/db/migrate/20130913015118_add_referral_code_to_order.rb +++ b/db/migrate/20130913015118_add_referral_code_to_order.rb @@ -1,4 +1,4 @@ -class AddReferralCodeToOrder < ActiveRecord::Migration +class AddReferralCodeToOrder < ActiveRecord::Migration[4.2] def change add_column :orders, :referral_code, :string end diff --git a/db/migrate/20130917053547_create_harvests.rb b/db/migrate/20130917053547_create_harvests.rb index 928f60d08..f7e529a30 100644 --- a/db/migrate/20130917053547_create_harvests.rb +++ b/db/migrate/20130917053547_create_harvests.rb @@ -1,4 +1,4 @@ -class CreateHarvests < ActiveRecord::Migration +class CreateHarvests < ActiveRecord::Migration[4.2] def change create_table :harvests do |t| t.integer :crop_id, null: false diff --git a/db/migrate/20130917060257_change_harvest_notes_to_description.rb b/db/migrate/20130917060257_change_harvest_notes_to_description.rb index bac3167cf..8359c0b9c 100644 --- a/db/migrate/20130917060257_change_harvest_notes_to_description.rb +++ b/db/migrate/20130917060257_change_harvest_notes_to_description.rb @@ -1,4 +1,4 @@ -class ChangeHarvestNotesToDescription < ActiveRecord::Migration +class ChangeHarvestNotesToDescription < ActiveRecord::Migration[4.2] def change rename_column :harvests, :notes, :description end diff --git a/db/migrate/20130917071545_change_harvest_units_to_unit.rb b/db/migrate/20130917071545_change_harvest_units_to_unit.rb index d843c2c3d..7f859f47d 100644 --- a/db/migrate/20130917071545_change_harvest_units_to_unit.rb +++ b/db/migrate/20130917071545_change_harvest_units_to_unit.rb @@ -1,4 +1,4 @@ -class ChangeHarvestUnitsToUnit < ActiveRecord::Migration +class ChangeHarvestUnitsToUnit < ActiveRecord::Migration[4.2] def change rename_column :harvests, :units, :unit end diff --git a/db/migrate/20130917075803_add_slug_to_harvests.rb b/db/migrate/20130917075803_add_slug_to_harvests.rb index 21b44d0ae..b9bc376e0 100644 --- a/db/migrate/20130917075803_add_slug_to_harvests.rb +++ b/db/migrate/20130917075803_add_slug_to_harvests.rb @@ -1,4 +1,4 @@ -class AddSlugToHarvests < ActiveRecord::Migration +class AddSlugToHarvests < ActiveRecord::Migration[4.2] def change add_column :harvests, :slug, :string end diff --git a/db/migrate/20130925050304_add_weight_to_harvests.rb b/db/migrate/20130925050304_add_weight_to_harvests.rb index caf1fd87a..1aa43df6b 100644 --- a/db/migrate/20130925050304_add_weight_to_harvests.rb +++ b/db/migrate/20130925050304_add_weight_to_harvests.rb @@ -1,4 +1,4 @@ -class AddWeightToHarvests < ActiveRecord::Migration +class AddWeightToHarvests < ActiveRecord::Migration[4.2] def change add_column :harvests, :weight_quantity, :decimal add_column :harvests, :weight_unit, :string diff --git a/db/migrate/20131018101204_rename_system_name_to_name.rb b/db/migrate/20131018101204_rename_system_name_to_name.rb index 41f810a5d..6124d1746 100644 --- a/db/migrate/20131018101204_rename_system_name_to_name.rb +++ b/db/migrate/20131018101204_rename_system_name_to_name.rb @@ -1,4 +1,4 @@ -class RenameSystemNameToName < ActiveRecord::Migration +class RenameSystemNameToName < ActiveRecord::Migration[4.2] def up # Rails is smart enough to alter the column being indexed, but not the name # of the index, and there's no rename_index command. diff --git a/db/migrate/20131025104228_add_fields_to_gardens.rb b/db/migrate/20131025104228_add_fields_to_gardens.rb index 2c3983507..e77a9c6c9 100644 --- a/db/migrate/20131025104228_add_fields_to_gardens.rb +++ b/db/migrate/20131025104228_add_fields_to_gardens.rb @@ -1,4 +1,4 @@ -class AddFieldsToGardens < ActiveRecord::Migration +class AddFieldsToGardens < ActiveRecord::Migration[4.2] def change add_column :gardens, :active, :boolean, default: true add_column :gardens, :location, :string diff --git a/db/migrate/20131029053113_add_plant_part_to_harvests.rb b/db/migrate/20131029053113_add_plant_part_to_harvests.rb index 0619bbb3e..fad21613f 100644 --- a/db/migrate/20131029053113_add_plant_part_to_harvests.rb +++ b/db/migrate/20131029053113_add_plant_part_to_harvests.rb @@ -1,4 +1,4 @@ -class AddPlantPartToHarvests < ActiveRecord::Migration +class AddPlantPartToHarvests < ActiveRecord::Migration[4.2] def change add_column :harvests, :plant_part, :string end diff --git a/db/migrate/20131030230908_create_plant_parts.rb b/db/migrate/20131030230908_create_plant_parts.rb index 0772a22b2..f3454f978 100644 --- a/db/migrate/20131030230908_create_plant_parts.rb +++ b/db/migrate/20131030230908_create_plant_parts.rb @@ -1,4 +1,4 @@ -class CreatePlantParts < ActiveRecord::Migration +class CreatePlantParts < ActiveRecord::Migration[4.2] def change create_table :plant_parts do |t| t.string :name diff --git a/db/migrate/20131030231202_change_plant_part_to_plant_part_id.rb b/db/migrate/20131030231202_change_plant_part_to_plant_part_id.rb index cfadbf1c6..134eeca97 100644 --- a/db/migrate/20131030231202_change_plant_part_to_plant_part_id.rb +++ b/db/migrate/20131030231202_change_plant_part_to_plant_part_id.rb @@ -1,4 +1,4 @@ -class ChangePlantPartToPlantPartId < ActiveRecord::Migration +class ChangePlantPartToPlantPartId < ActiveRecord::Migration[4.2] def up remove_column :harvests, :plant_part add_column :harvests, :plant_part_id, :integer diff --git a/db/migrate/20131031000655_add_slug_to_plant_part.rb b/db/migrate/20131031000655_add_slug_to_plant_part.rb index 9af39cec6..242c30776 100644 --- a/db/migrate/20131031000655_add_slug_to_plant_part.rb +++ b/db/migrate/20131031000655_add_slug_to_plant_part.rb @@ -1,4 +1,4 @@ -class AddSlugToPlantPart < ActiveRecord::Migration +class AddSlugToPlantPart < ActiveRecord::Migration[4.2] def change add_column :plant_parts, :slug, :string end diff --git a/db/migrate/20140718075753_default_plantings_count_to_zero.rb b/db/migrate/20140718075753_default_plantings_count_to_zero.rb index ce8d0d075..15ee6df8d 100644 --- a/db/migrate/20140718075753_default_plantings_count_to_zero.rb +++ b/db/migrate/20140718075753_default_plantings_count_to_zero.rb @@ -1,4 +1,4 @@ -class DefaultPlantingsCountToZero < ActiveRecord::Migration +class DefaultPlantingsCountToZero < ActiveRecord::Migration[4.2] def up change_column :crops, :plantings_count, :integer, default: 0 end diff --git a/db/migrate/20140829230600_add_finished_to_planting.rb b/db/migrate/20140829230600_add_finished_to_planting.rb index f8cb41663..868298f6d 100644 --- a/db/migrate/20140829230600_add_finished_to_planting.rb +++ b/db/migrate/20140829230600_add_finished_to_planting.rb @@ -1,4 +1,4 @@ -class AddFinishedToPlanting < ActiveRecord::Migration +class AddFinishedToPlanting < ActiveRecord::Migration[4.2] def change add_column :plantings, :finished, :boolean, default: false add_column :plantings, :finished_at, :date diff --git a/db/migrate/20140905001730_add_harvests_photos_table.rb b/db/migrate/20140905001730_add_harvests_photos_table.rb index fb9c73a37..f05ae0135 100644 --- a/db/migrate/20140905001730_add_harvests_photos_table.rb +++ b/db/migrate/20140905001730_add_harvests_photos_table.rb @@ -1,4 +1,4 @@ -class AddHarvestsPhotosTable < ActiveRecord::Migration +class AddHarvestsPhotosTable < ActiveRecord::Migration[4.2] def change create_table :harvests_photos, id: false do |t| t.integer :photo_id diff --git a/db/migrate/20140928044231_add_crops_posts_table.rb b/db/migrate/20140928044231_add_crops_posts_table.rb index 6dcf9a494..a9e8761f9 100644 --- a/db/migrate/20140928044231_add_crops_posts_table.rb +++ b/db/migrate/20140928044231_add_crops_posts_table.rb @@ -1,4 +1,4 @@ -class AddCropsPostsTable < ActiveRecord::Migration +class AddCropsPostsTable < ActiveRecord::Migration[4.2] def change create_table :crops_posts, id: false do |t| t.integer :crop_id diff --git a/db/migrate/20140928085713_add_send_planting_reminder_to_member.rb b/db/migrate/20140928085713_add_send_planting_reminder_to_member.rb index 67c7184d7..cd464eeed 100644 --- a/db/migrate/20140928085713_add_send_planting_reminder_to_member.rb +++ b/db/migrate/20140928085713_add_send_planting_reminder_to_member.rb @@ -1,4 +1,4 @@ -class AddSendPlantingReminderToMember < ActiveRecord::Migration +class AddSendPlantingReminderToMember < ActiveRecord::Migration[4.2] def change add_column :members, :send_planting_reminder, :boolean, default: true end diff --git a/db/migrate/20141002022459_create_index_harvest_photos.rb b/db/migrate/20141002022459_create_index_harvest_photos.rb index 95913bd04..f9c8ee911 100644 --- a/db/migrate/20141002022459_create_index_harvest_photos.rb +++ b/db/migrate/20141002022459_create_index_harvest_photos.rb @@ -1,4 +1,4 @@ -class CreateIndexHarvestPhotos < ActiveRecord::Migration +class CreateIndexHarvestPhotos < ActiveRecord::Migration[4.2] def change add_index(:harvests_photos, %i(harvest_id photo_id)) end diff --git a/db/migrate/20141018111015_create_alternate_names.rb b/db/migrate/20141018111015_create_alternate_names.rb index facb96100..65400cc22 100644 --- a/db/migrate/20141018111015_create_alternate_names.rb +++ b/db/migrate/20141018111015_create_alternate_names.rb @@ -1,4 +1,4 @@ -class CreateAlternateNames < ActiveRecord::Migration +class CreateAlternateNames < ActiveRecord::Migration[4.2] def change create_table :alternate_names do |t| t.string :name, null: false diff --git a/db/migrate/20141111130849_create_follows.rb b/db/migrate/20141111130849_create_follows.rb index 9c3f03b74..5ca3f9b12 100644 --- a/db/migrate/20141111130849_create_follows.rb +++ b/db/migrate/20141111130849_create_follows.rb @@ -1,4 +1,4 @@ -class CreateFollows < ActiveRecord::Migration +class CreateFollows < ActiveRecord::Migration[4.2] def change create_table :follows do |t| t.integer :member_id diff --git a/db/migrate/20141119130555_change_follows_member_id_to_follower_id.rb b/db/migrate/20141119130555_change_follows_member_id_to_follower_id.rb index cb9e58d3a..a777fc79f 100644 --- a/db/migrate/20141119130555_change_follows_member_id_to_follower_id.rb +++ b/db/migrate/20141119130555_change_follows_member_id_to_follower_id.rb @@ -1,4 +1,4 @@ -class ChangeFollowsMemberIdToFollowerId < ActiveRecord::Migration +class ChangeFollowsMemberIdToFollowerId < ActiveRecord::Migration[4.2] def change rename_column :follows, :member_id, :follower_id end diff --git a/db/migrate/20150124110540_add_properties_to_seeds.rb b/db/migrate/20150124110540_add_properties_to_seeds.rb index 7d60735fd..0adb96600 100644 --- a/db/migrate/20150124110540_add_properties_to_seeds.rb +++ b/db/migrate/20150124110540_add_properties_to_seeds.rb @@ -1,4 +1,4 @@ -class AddPropertiesToSeeds < ActiveRecord::Migration +class AddPropertiesToSeeds < ActiveRecord::Migration[4.2] def change add_column :seeds, :days_until_maturity_min, :integer add_column :seeds, :days_until_maturity_max, :integer diff --git a/db/migrate/20150127043022_add_gardens_photos_table.rb b/db/migrate/20150127043022_add_gardens_photos_table.rb index 0c2b99dfe..459174245 100644 --- a/db/migrate/20150127043022_add_gardens_photos_table.rb +++ b/db/migrate/20150127043022_add_gardens_photos_table.rb @@ -1,4 +1,4 @@ -class AddGardensPhotosTable < ActiveRecord::Migration +class AddGardensPhotosTable < ActiveRecord::Migration[4.2] def change create_table :gardens_photos, id: false do |t| t.integer :photo_id diff --git a/db/migrate/20150129034206_add_si_weight_to_harvest.rb b/db/migrate/20150129034206_add_si_weight_to_harvest.rb index b1e532d8d..881d4ad01 100644 --- a/db/migrate/20150129034206_add_si_weight_to_harvest.rb +++ b/db/migrate/20150129034206_add_si_weight_to_harvest.rb @@ -1,4 +1,4 @@ -class AddSiWeightToHarvest < ActiveRecord::Migration +class AddSiWeightToHarvest < ActiveRecord::Migration[4.2] def change add_column :harvests, :si_weight, :float end diff --git a/db/migrate/20150130224814_add_requester_to_crops.rb b/db/migrate/20150130224814_add_requester_to_crops.rb index f284a6f68..0d9f70943 100644 --- a/db/migrate/20150130224814_add_requester_to_crops.rb +++ b/db/migrate/20150130224814_add_requester_to_crops.rb @@ -1,4 +1,4 @@ -class AddRequesterToCrops < ActiveRecord::Migration +class AddRequesterToCrops < ActiveRecord::Migration[4.2] def change add_column :crops, :requester_id, :integer add_index :crops, :requester_id diff --git a/db/migrate/20150201052245_create_cms.rb b/db/migrate/20150201052245_create_cms.rb index e8ba4fb9e..91fea54e8 100644 --- a/db/migrate/20150201052245_create_cms.rb +++ b/db/migrate/20150201052245_create_cms.rb @@ -1,4 +1,4 @@ -class CreateCms < ActiveRecord::Migration +class CreateCms < ActiveRecord::Migration[4.2] def self.up # rubocop:disable Metrics/MethodLength, Metrics/AbcSize text_limit = case ActiveRecord::Base.connection.adapter_name when 'PostgreSQL' diff --git a/db/migrate/20150201053200_add_approval_status_to_crops.rb b/db/migrate/20150201053200_add_approval_status_to_crops.rb index 07b1e0879..ff6b12cbb 100644 --- a/db/migrate/20150201053200_add_approval_status_to_crops.rb +++ b/db/migrate/20150201053200_add_approval_status_to_crops.rb @@ -1,4 +1,4 @@ -class AddApprovalStatusToCrops < ActiveRecord::Migration +class AddApprovalStatusToCrops < ActiveRecord::Migration[4.2] def change add_column :crops, :approval_status, :string, default: "approved" end diff --git a/db/migrate/20150201062506_add_reason_for_rejection_to_crops.rb b/db/migrate/20150201062506_add_reason_for_rejection_to_crops.rb index 0fd283a67..001cd795b 100644 --- a/db/migrate/20150201062506_add_reason_for_rejection_to_crops.rb +++ b/db/migrate/20150201062506_add_reason_for_rejection_to_crops.rb @@ -1,4 +1,4 @@ -class AddReasonForRejectionToCrops < ActiveRecord::Migration +class AddReasonForRejectionToCrops < ActiveRecord::Migration[4.2] def change add_column :crops, :reason_for_rejection, :text end diff --git a/db/migrate/20150201064502_add_request_notes_to_crops.rb b/db/migrate/20150201064502_add_request_notes_to_crops.rb index 0720bf4cb..433c5ad35 100644 --- a/db/migrate/20150201064502_add_request_notes_to_crops.rb +++ b/db/migrate/20150201064502_add_request_notes_to_crops.rb @@ -1,4 +1,4 @@ -class AddRequestNotesToCrops < ActiveRecord::Migration +class AddRequestNotesToCrops < ActiveRecord::Migration[4.2] def change add_column :crops, :request_notes, :text end diff --git a/db/migrate/20150203080226_create_likes.rb b/db/migrate/20150203080226_create_likes.rb index 71960699c..b5f7d427b 100644 --- a/db/migrate/20150203080226_create_likes.rb +++ b/db/migrate/20150203080226_create_likes.rb @@ -1,4 +1,4 @@ -class CreateLikes < ActiveRecord::Migration +class CreateLikes < ActiveRecord::Migration[4.2] def change create_table :likes do |t| t.references :member, index: true diff --git a/db/migrate/20150209105410_add_rejection_notes_to_crops.rb b/db/migrate/20150209105410_add_rejection_notes_to_crops.rb index 59239fd55..44fc9fb7b 100644 --- a/db/migrate/20150209105410_add_rejection_notes_to_crops.rb +++ b/db/migrate/20150209105410_add_rejection_notes_to_crops.rb @@ -1,4 +1,4 @@ -class AddRejectionNotesToCrops < ActiveRecord::Migration +class AddRejectionNotesToCrops < ActiveRecord::Migration[4.2] def change add_column :crops, :rejection_notes, :text end diff --git a/db/migrate/20150625224805_add_days_before_maturity_to_plantings.rb b/db/migrate/20150625224805_add_days_before_maturity_to_plantings.rb index 0ddf5acb5..c60e6f6a3 100644 --- a/db/migrate/20150625224805_add_days_before_maturity_to_plantings.rb +++ b/db/migrate/20150625224805_add_days_before_maturity_to_plantings.rb @@ -1,4 +1,4 @@ -class AddDaysBeforeMaturityToPlantings < ActiveRecord::Migration +class AddDaysBeforeMaturityToPlantings < ActiveRecord::Migration[4.2] def change add_column :plantings, :days_before_maturity, :integer end diff --git a/db/migrate/20150824145414_add_member_preferred_image.rb b/db/migrate/20150824145414_add_member_preferred_image.rb index dc24bd5a0..c3c563374 100644 --- a/db/migrate/20150824145414_add_member_preferred_image.rb +++ b/db/migrate/20150824145414_add_member_preferred_image.rb @@ -1,4 +1,4 @@ -class AddMemberPreferredImage < ActiveRecord::Migration +class AddMemberPreferredImage < ActiveRecord::Migration[4.2] def change add_column :members, :preferred_avatar_uri, :string end diff --git a/db/migrate/20161129021533_rename_scientific_name.rb b/db/migrate/20161129021533_rename_scientific_name.rb index e2ff2ea0e..3aac2e5d4 100644 --- a/db/migrate/20161129021533_rename_scientific_name.rb +++ b/db/migrate/20161129021533_rename_scientific_name.rb @@ -1,4 +1,4 @@ -class RenameScientificName < ActiveRecord::Migration +class RenameScientificName < ActiveRecord::Migration[4.2] def self.up rename_column :scientific_names, :scientific_name, :name end diff --git a/db/migrate/20161201154922_add_photos_seeds_table.rb b/db/migrate/20161201154922_add_photos_seeds_table.rb index f4a2f18fe..bbe7478c5 100644 --- a/db/migrate/20161201154922_add_photos_seeds_table.rb +++ b/db/migrate/20161201154922_add_photos_seeds_table.rb @@ -1,4 +1,4 @@ -class AddPhotosSeedsTable < ActiveRecord::Migration +class AddPhotosSeedsTable < ActiveRecord::Migration[4.2] def change create_table :photos_seeds, id: false do |t| t.integer :photo_id diff --git a/db/migrate/20170104035248_add_planting_ref_to_harvests.rb b/db/migrate/20170104035248_add_planting_ref_to_harvests.rb index 57935b073..515ffaa83 100644 --- a/db/migrate/20170104035248_add_planting_ref_to_harvests.rb +++ b/db/migrate/20170104035248_add_planting_ref_to_harvests.rb @@ -1,4 +1,4 @@ -class AddPlantingRefToHarvests < ActiveRecord::Migration +class AddPlantingRefToHarvests < ActiveRecord::Migration[4.2] def change add_reference :harvests, :planting, index: true, foreign_key: true end diff --git a/db/migrate/20170413221549_counter_caches.rb b/db/migrate/20170413221549_counter_caches.rb index 45624ea44..27f4f1f6b 100644 --- a/db/migrate/20170413221549_counter_caches.rb +++ b/db/migrate/20170413221549_counter_caches.rb @@ -1,4 +1,4 @@ -class CounterCaches < ActiveRecord::Migration +class CounterCaches < ActiveRecord::Migration[4.2] def change add_column :members, :gardens_count, :integer add_column :members, :harvests_count, :integer diff --git a/db/migrate/20170520060252_add_deleted_to_members.rb b/db/migrate/20170520060252_add_deleted_to_members.rb index 211989869..3e7059aec 100644 --- a/db/migrate/20170520060252_add_deleted_to_members.rb +++ b/db/migrate/20170520060252_add_deleted_to_members.rb @@ -1,4 +1,4 @@ -class AddDeletedToMembers < ActiveRecord::Migration +class AddDeletedToMembers < ActiveRecord::Migration[4.2] def change add_column :members, :deleted_at, :datetime add_index :members, :deleted_at diff --git a/db/migrate/20171022032108_all_the_predictions.rb b/db/migrate/20171022032108_all_the_predictions.rb index c4338ad2a..aa0f1bfde 100644 --- a/db/migrate/20171022032108_all_the_predictions.rb +++ b/db/migrate/20171022032108_all_the_predictions.rb @@ -1,4 +1,4 @@ -class AllThePredictions < ActiveRecord::Migration +class AllThePredictions < ActiveRecord::Migration[4.2] def change add_column :crops, :perennial, :boolean, default: false diff --git a/db/migrate/20171028230429_create_median_function.rb b/db/migrate/20171028230429_create_median_function.rb index e565a4f68..5e57c01d1 100644 --- a/db/migrate/20171028230429_create_median_function.rb +++ b/db/migrate/20171028230429_create_median_function.rb @@ -1,4 +1,4 @@ -class CreateMedianFunction < ActiveRecord::Migration +class CreateMedianFunction < ActiveRecord::Migration[4.2] def up ActiveMedian.create_function end diff --git a/db/migrate/20171105011017_set_prediction_data.rb b/db/migrate/20171105011017_set_prediction_data.rb index 149001927..d964c8848 100644 --- a/db/migrate/20171105011017_set_prediction_data.rb +++ b/db/migrate/20171105011017_set_prediction_data.rb @@ -1,4 +1,4 @@ -class SetPredictionData < ActiveRecord::Migration +class SetPredictionData < ActiveRecord::Migration[4.2] def up say "Updating all plantings time to first harvest" Planting.all.each(&:update_harvest_days) diff --git a/db/migrate/20171129041341_create_photographings.rb b/db/migrate/20171129041341_create_photographings.rb index a18ab3680..a0563fa3c 100644 --- a/db/migrate/20171129041341_create_photographings.rb +++ b/db/migrate/20171129041341_create_photographings.rb @@ -1,4 +1,4 @@ -class CreatePhotographings < ActiveRecord::Migration +class CreatePhotographings < ActiveRecord::Migration[4.2] def change create_table :photographings do |t| t.integer :photo_id, null: false diff --git a/db/schema.rb b/db/schema.rb index ea66548f1..dec08cc13 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180103024400) do +ActiveRecord::Schema.define(version: 20171129041341) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -54,8 +54,8 @@ ActiveRecord::Schema.define(version: 20180103024400) do create_table "comfy_cms_blocks", id: :serial, force: :cascade do |t| t.string "identifier", null: false t.text "content" - t.integer "blockable_id" t.string "blockable_type" + t.integer "blockable_id" t.datetime "created_at" t.datetime "updated_at" t.index ["blockable_id", "blockable_type"], name: "index_comfy_cms_blocks_on_blockable_id_and_blockable_type" @@ -265,8 +265,8 @@ ActiveRecord::Schema.define(version: 20180103024400) do create_table "likes", id: :serial, force: :cascade do |t| t.integer "member_id" - t.integer "likeable_id" t.string "likeable_type" + t.integer "likeable_id" t.string "categories", array: true t.datetime "created_at" t.datetime "updated_at" @@ -421,9 +421,6 @@ ActiveRecord::Schema.define(version: 20180103024400) do t.integer "lifespan" t.integer "days_to_first_harvest" t.integer "days_to_last_harvest" - t.boolean "failed" - t.text "failure_cause" - t.index ["failed"], name: "index_plantings_on_failed" t.index ["slug"], name: "index_plantings_on_slug", unique: true end From 2fd13e9b7c92372ac545e8eee678d90a7c7a30d1 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 4 Jan 2018 14:44:52 +1300 Subject: [PATCH 032/219] Updated new path to jquery autocomplete --- app/assets/javascripts/application.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 0ad04350c..78d273c6c 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -15,7 +15,7 @@ //= require js-routes //= require jquery //= require jquery_ujs -//= require jquery-ui/autocomplete +//= require jquery-ui/widgets/autocomplete //= require bootstrap-sprockets //= require bootstrap-datepicker //= require_tree . From 090bc0226925ea0bd64faef6450f65893c82ce71 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 4 Jan 2018 14:55:36 +1300 Subject: [PATCH 033/219] `belongs_to` defaults to required. setting some to optional --- app/models/crop.rb | 4 ++-- app/models/harvest.rb | 2 +- app/models/post.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/crop.rb b/app/models/crop.rb index a961daf58..f87fbb262 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -17,8 +17,8 @@ class Crop < ApplicationRecord has_many :harvests has_many :plant_parts, -> { uniq.reorder("plant_parts.name") }, through: :harvests belongs_to :creator, class_name: 'Member' - belongs_to :requester, class_name: 'Member' - belongs_to :parent, class_name: 'Crop' + belongs_to :requester, class_name: 'Member', optional: true + belongs_to :parent, class_name: 'Crop', optional: true has_many :varieties, class_name: 'Crop', foreign_key: 'parent_id' has_and_belongs_to_many :posts # rubocop:disable Rails/HasAndBelongsToMany diff --git a/app/models/harvest.rb b/app/models/harvest.rb index a5328ef49..a58eaa991 100644 --- a/app/models/harvest.rb +++ b/app/models/harvest.rb @@ -35,7 +35,7 @@ class Harvest < ApplicationRecord belongs_to :crop belongs_to :owner, class_name: 'Member', counter_cache: true belongs_to :plant_part - belongs_to :planting + belongs_to :planting, optional: true ## ## Scopes diff --git a/app/models/post.rb b/app/models/post.rb index ae27dbb50..2dc254741 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -6,7 +6,7 @@ class Post < ApplicationRecord # # Relationships belongs_to :author, class_name: 'Member' - belongs_to :forum + belongs_to :forum, optional: true has_many :comments, dependent: :destroy has_and_belongs_to_many :crops # rubocop:disable Rails/HasAndBelongsToMany # also has_many notifications, but kinda meaningless to get at them From 79f6f7097aace417ed1bd0cf1716b9f0a6f57df6 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 4 Jan 2018 15:01:09 +1300 Subject: [PATCH 034/219] updated npm package.json --- package-lock.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 013886a6b..7ed4e60ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -149,7 +149,7 @@ "circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "integrity": "sha1-gVyZ6oT2gJUp0vRXkb34JxE1LWY=", "dev": true }, "cli": { @@ -212,7 +212,7 @@ "coffeescript": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.0.3.tgz", - "integrity": "sha512-iIfUN+71IyI2FQABXh1luzZeQgqwUPeWh6lDovJatQQs+30bvyGnBY0r4BnD0hoMAasNuZVHlL1U09Oy1ZfSeg==", + "integrity": "sha1-dg8Cck9fCRG+fO+jSo4OEMXYUSo=", "dev": true }, "concat-map": { @@ -519,7 +519,7 @@ "esprima": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "integrity": "sha1-RJnt3NERDgshi6zy+n9/WfVcqAQ=", "dev": true }, "esquery": { @@ -852,7 +852,7 @@ "js-yaml": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", - "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", + "integrity": "sha1-LnhEFka9RoLpY/IrbpKCPDCcYtw=", "dev": true, "requires": { "argparse": "1.0.9", @@ -1113,7 +1113,7 @@ "readable-stream": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", "dev": true, "requires": { "core-util-is": "1.0.2", @@ -1191,7 +1191,7 @@ "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", "dev": true, "requires": { "glob": "7.1.2" @@ -1215,7 +1215,7 @@ "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=", "dev": true }, "shelljs": { @@ -1255,7 +1255,7 @@ "string_decoder": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", "dev": true, "requires": { "safe-buffer": "5.1.1" From 53c9f89bc1b81ab99a3562e1185c7dfa1b9530fc Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 4 Jan 2018 15:11:36 +1300 Subject: [PATCH 035/219] Fixed up tabulation --- app/assets/javascripts/graphs/width_scale.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/graphs/width_scale.js b/app/assets/javascripts/graphs/width_scale.js index 4ba275eda..7f2b9dda2 100644 --- a/app/assets/javascripts/graphs/width_scale.js +++ b/app/assets/javascripts/graphs/width_scale.js @@ -29,8 +29,8 @@ WidthScale.prototype.getMaxValue = function() { return d3.max(this._data.bars.map(function(bar) { - return bar.value; -})); + return bar.value; + })); }; growstuff.WidthScale = WidthScale; From 46cfe527bc56ca0074ceafb2224c471c9a609541 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 4 Jan 2018 16:38:09 +1300 Subject: [PATCH 036/219] add in gem 'rails-controller-testing' --- Gemfile | 1 + Gemfile.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/Gemfile b/Gemfile index eb461c054..32158db8f 100644 --- a/Gemfile +++ b/Gemfile @@ -150,6 +150,7 @@ end group :test do gem 'codeclimate-test-reporter', require: false + gem 'rails-controller-testing' gem 'timecop' end diff --git a/Gemfile.lock b/Gemfile.lock index bdd2bc94e..28e049445 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -344,6 +344,10 @@ GEM rails-assets-leaflet (1.2.0) rails-assets-leaflet.markercluster (1.2.0) rails-assets-leaflet (>= 1.0.3) + rails-controller-testing (1.0.2) + actionpack (~> 5.x, >= 5.0.1) + actionview (~> 5.x, >= 5.0.1) + activesupport (~> 5.x) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) @@ -542,6 +546,7 @@ DEPENDENCIES poltergeist rails (= 5.1.4) rails-assets-leaflet.markercluster! + rails-controller-testing rails_12factor rainbow (< 2.2.0) rake (>= 10.0.0) From 2f9610a60fd2c2cfefb4c0d856f9687f48a30891 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 4 Jan 2018 17:23:10 +1300 Subject: [PATCH 037/219] Http positional arguments in rails 5 --- .rubocop.yml | 4 -- .../admin/orders_controller_spec.rb | 4 +- spec/controllers/admin_controller_spec.rb | 15 +++-- spec/controllers/comments_controller_spec.rb | 10 ++-- spec/controllers/gardens_controller_spec.rb | 14 ++--- spec/controllers/harvests_controller_spec.rb | 44 +++++++------- spec/controllers/likes_controller_spec.rb | 4 +- spec/controllers/member_controller_spec.rb | 16 ++--- .../notifications_controller_spec.rb | 14 ++--- .../order_items_controller_spec.rb | 16 +++-- spec/controllers/orders_controller_spec.rb | 8 +-- .../photo_associations_controller_spec.rb | 6 +- spec/controllers/photos_controller_spec.rb | 58 ++++++++++++------- spec/controllers/places_controller_spec.rb | 6 +- spec/controllers/plantings_controller_spec.rb | 20 +++---- spec/controllers/posts_controller_spec.rb | 14 ++--- spec/controllers/roles_controller_spec.rb | 2 +- .../scientific_names_controller_spec.rb | 8 +-- spec/controllers/seeds_controller_spec.rb | 8 +-- spec/controllers/shop_controller_spec.rb | 4 +- 20 files changed, 145 insertions(+), 130 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 545e1c1fa..571a090bd 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -39,10 +39,6 @@ Layout/AlignParameters: Metrics/LineLength: Max: 120 -# turn these back on in Rails 5 -Rails/HttpPositionalArguments: # See https://github.com/bbatsov/rubocop/issues/3629 - Enabled: false - Style/Documentation: Enabled: false diff --git a/spec/controllers/admin/orders_controller_spec.rb b/spec/controllers/admin/orders_controller_spec.rb index fb475b34c..207112f39 100644 --- a/spec/controllers/admin/orders_controller_spec.rb +++ b/spec/controllers/admin/orders_controller_spec.rb @@ -6,12 +6,12 @@ describe Admin::OrdersController do describe "GET search" do it "assigns @orders" do order = FactoryBot.create(:order) - get :search, search_by: 'order_id', search_text: order.id + get :search, params: { search_by: 'order_id', search_text: order.id } assigns(:orders).should eq([order]) end it "sets an error message if nothing found" do - get :search, search_by: 'order_id', search_text: 'foo' + get :search, params: { search_by: 'order_id', search_text: 'foo' } flash[:alert].should have_text "Couldn't find order with" end end diff --git a/spec/controllers/admin_controller_spec.rb b/spec/controllers/admin_controller_spec.rb index 9f134bbab..5aa87e7cb 100644 --- a/spec/controllers/admin_controller_spec.rb +++ b/spec/controllers/admin_controller_spec.rb @@ -4,16 +4,15 @@ describe AdminController do login_member(:admin_member) describe "GET admin/newsletter" do - it 'fetches the admin newsletter page' do - get :newsletter - response.should be_success - response.should render_template("admin/newsletter") + before { get :newsletter } + describe 'fetches the admin newsletter page' do + it { expect(response).to be_success } + it { expect(response).to render_template("admin/newsletter") } end - it 'assigns @members' do - m = FactoryBot.create(:newsletter_recipient_member) - get :newsletter - assigns(:members).should eq [m] + describe 'assigns @members' do + let!(:m) { FactoryBot.create(:newsletter_recipient_member) } + it { expect(assigns(:members)).to eq [m] } end end end diff --git a/spec/controllers/comments_controller_spec.rb b/spec/controllers/comments_controller_spec.rb index 0e986cc93..6b2383b36 100644 --- a/spec/controllers/comments_controller_spec.rb +++ b/spec/controllers/comments_controller_spec.rb @@ -29,7 +29,7 @@ describe CommentsController do let(:post) { FactoryBot.create(:post) } describe "with valid params" do - before { get :new, post_id: post.id } + before { get :new, params: { post_id: post.id } } it "picks up post from params" do assigns(:post).should eq(post) @@ -49,7 +49,7 @@ describe CommentsController do describe "GET edit" do let(:post) { FactoryBot.create(:post) } - before { get :edit, id: comment.to_param } + before { get :edit, params: { id: comment.to_param } } describe "my comment" do let!(:comment) { FactoryBot.create :comment, author: member, post: post } @@ -66,7 +66,7 @@ describe CommentsController do end describe "PUT update" do - before { put :update, id: comment.to_param, comment: valid_attributes } + before { put :update, params: { id: comment.to_param, comment: valid_attributes } } describe "my comment" do let(:comment) { FactoryBot.create :comment, author: member } @@ -74,10 +74,12 @@ describe CommentsController do expect(response).to redirect_to(comment.post) end end + describe "not my comment" do let(:comment) { FactoryBot.create :comment } it { expect(response).not_to be_success } end + describe "attempting to change post_id" do let(:post) { FactoryBot.create :post, subject: 'our post' } let(:other_post) { FactoryBot.create :post, subject: 'the other post' } @@ -91,7 +93,7 @@ describe CommentsController do end describe "DELETE destroy" do - before { delete :destroy, id: comment.to_param } + before { delete :destroy, params: { id: comment.to_param } } describe "my comment" do let(:comment) { FactoryBot.create :comment, author: member } diff --git a/spec/controllers/gardens_controller_spec.rb b/spec/controllers/gardens_controller_spec.rb index b0ccae0b8..f661a7299 100644 --- a/spec/controllers/gardens_controller_spec.rb +++ b/spec/controllers/gardens_controller_spec.rb @@ -7,7 +7,7 @@ RSpec.describe GardensController, type: :controller do context "when not signed in" do let(:garden) { double('garden') } describe 'GET new' do - before { get :new, id: garden.to_param } + before { get :new, params: { id: garden.to_param } } it { expect(response).to redirect_to(new_member_session_path) } end describe 'PUT create' do @@ -25,15 +25,15 @@ RSpec.describe GardensController, type: :controller do expect(garden).not_to receive(:destroy) end describe 'GET edit' do - before { get :edit, id: garden.to_param } + before { get :edit, params: { id: garden.to_param } } it { expect(response).to redirect_to(new_member_session_path) } end describe 'POST update' do - before { post :update, id: garden.to_param, garden: valid_params } + before { post :update, params: { id: garden.to_param, garden: valid_params } } it { expect(response).to redirect_to(new_member_session_path) } end describe 'DELETE' do - before { delete :destroy, id: garden.to_param, params: { garden: valid_params } } + before { delete :destroy, params: { id: garden.to_param, params: { garden: valid_params } } } it { expect(response).to redirect_to(new_member_session_path) } end end @@ -55,15 +55,15 @@ RSpec.describe GardensController, type: :controller do end describe 'GET edit' do - before { get :edit, id: not_my_garden.to_param } + before { get :edit, params: { id: not_my_garden.to_param } } it { expect(response).to redirect_to(root_path) } end describe 'POST update' do - before { post :update, id: not_my_garden.to_param, garden: valid_params } + before { post :update, params: { id: not_my_garden.to_param, garden: valid_params } } it { expect(response).to redirect_to(root_path) } end describe 'DELETE' do - before { delete :destroy, id: not_my_garden.to_param, params: { garden: valid_params } } + before { delete :destroy, params: { id: not_my_garden.to_param, params: { garden: valid_params } } } it { expect(response).to redirect_to(root_path) } end end diff --git a/spec/controllers/harvests_controller_spec.rb b/spec/controllers/harvests_controller_spec.rb index 8a2edb56b..fca0bb840 100644 --- a/spec/controllers/harvests_controller_spec.rb +++ b/spec/controllers/harvests_controller_spec.rb @@ -21,18 +21,18 @@ describe HarvestsController do let(:harvest2) { FactoryBot.create(:harvest, owner_id: member2.id, crop_id: maize.id) } describe "assigns all harvests as @harvests" do - before { get :index, {} } + before { get :index, params: {} } it { assigns(:harvests).should =~ [harvest1, harvest2] } end describe "picks up owner from params and shows owner's harvests only" do - before { get :index, owner: member1.slug } + before { get :index, params: { owner: member1.slug } } it { expect(assigns(:owner)).to eq member1 } it { expect(assigns(:harvests)).to eq [harvest1] } end describe "picks up crop from params and shows the harvests for the crop only" do - before { get :index, crop: maize.name } + before { get :index, params: { crop: maize.name } } it { expect(assigns(:crop)).to eq maize } it { expect(assigns(:harvests)).to eq [harvest2] } end @@ -46,13 +46,13 @@ describe HarvestsController do describe "GET show" do let(:harvest) { Harvest.create! valid_attributes } describe "assigns the requested harvest as @harvest" do - before { get :show, id: harvest.to_param } + before { get :show, params: { id: harvest.to_param } } it { expect(assigns(:harvest)).to eq(harvest) } end end describe "GET new" do - before { get :new, {} } + before { get :new, params: {} } describe "assigns a new harvest as @harvest" do it { expect(assigns(:harvest)).to be_a_new(Harvest) } @@ -66,7 +66,7 @@ describe HarvestsController do describe "GET edit" do let(:harvest) { Harvest.create! valid_attributes } describe "assigns the requested harvest as @harvest" do - before { get :edit, id: harvest.to_param } + before { get :edit, params: { id: harvest.to_param } } it { expect(assigns(:harvest)).to eq(harvest) } end end @@ -75,24 +75,24 @@ describe HarvestsController do describe "with valid params" do it "creates a new Harvest" do expect do - post :create, harvest: valid_attributes + post :create, params: { harvest: valid_attributes } end.to change(Harvest, :count).by(1) end it "assigns a newly created harvest as @harvest" do - post :create, harvest: valid_attributes + post :create, params: { harvest: valid_attributes } assigns(:harvest).should be_a(Harvest) assigns(:harvest).should be_persisted end it "redirects to the created harvest" do - post :create, harvest: valid_attributes + post :create, params: { harvest: valid_attributes } response.should redirect_to(Harvest.last) end describe "links to planting" do let(:planting) { FactoryBot.create(:planting, owner_id: member.id, garden: member.gardens.first) } - before { post :create, harvest: valid_attributes.merge(planting_id: planting.id) } + before { post :create, params: { harvest: valid_attributes.merge(planting_id: planting.id) } } it { expect(Harvest.last.planting.id).to eq(planting.id) } end end @@ -101,13 +101,13 @@ describe HarvestsController do it "assigns a newly created but unsaved harvest as @harvest" do # Trigger the behavior that occurs when invalid params are submitted Harvest.any_instance.stub(:save).and_return(false) - post :create, harvest: { "crop_id" => "invalid value" } + post :create, params: { harvest: { "crop_id" => "invalid value" } } assigns(:harvest).should be_a_new(Harvest) end it "re-renders the 'new' template" do # Trigger the behavior that occurs when invalid params are submitted - post :create, harvest: { "crop_id" => "invalid value" } + post :create, params: { harvest: { "crop_id" => "invalid value" } } response.should render_template("new") end end @@ -119,7 +119,7 @@ describe HarvestsController do describe "does not save planting_id" do before do allow(Harvest).to receive(:new).and_return(harvest) - post :create, harvest: valid_attributes.merge(planting_id: not_my_planting.id) + post :create, params: { harvest: valid_attributes.merge(planting_id: not_my_planting.id) } end it { expect(harvest.planting_id).not_to eq(not_my_planting.id) } end @@ -135,18 +135,18 @@ describe HarvestsController do # receives the :update message with whatever params are # submitted in the request. Harvest.any_instance.should_receive(:update).with("crop_id" => "1", "owner_id": member.id) - put :update, id: harvest.to_param, harvest: { "crop_id" => "1" } + put :update, params: { id: harvest.to_param, harvest: { "crop_id" => "1" } } end it "assigns the requested harvest as @harvest" do harvest = Harvest.create! valid_attributes - put :update, id: harvest.to_param, harvest: valid_attributes + put :update, params: { id: harvest.to_param, harvest: valid_attributes } assigns(:harvest).should eq(harvest) end it "redirects to the harvest" do harvest = Harvest.create! valid_attributes - put :update, id: harvest.to_param, harvest: valid_attributes + put :update, params: { id: harvest.to_param, harvest: valid_attributes } response.should redirect_to(harvest) end end @@ -156,13 +156,13 @@ describe HarvestsController do harvest = Harvest.create! valid_attributes # Trigger the behavior that occurs when invalid params are submitted Harvest.any_instance.stub(:save).and_return(false) - put :update, id: harvest.to_param, harvest: { "crop_id" => "invalid value" } + put :update, params: { id: harvest.to_param, harvest: { "crop_id" => "invalid value" } } assigns(:harvest).should eq(harvest) end it "re-renders the 'edit' template" do harvest = Harvest.create! valid_attributes - put :update, id: harvest.to_param, harvest: { "crop_id" => "invalid value" } + put :update, params: { id: harvest.to_param, harvest: { "crop_id" => "invalid value" } } response.should render_template("edit") end end @@ -172,8 +172,8 @@ describe HarvestsController do let(:harvest) { FactoryBot.create(:harvest) } describe "does not save planting_id" do before do - put :update, id: harvest.to_param, - harvest: valid_attributes.merge(planting_id: not_my_planting.id) + put :update, params: { id: harvest.to_param, + harvest: valid_attributes.merge(planting_id: not_my_planting.id) } end it { expect(harvest.planting_id).to eq(nil) } end @@ -184,13 +184,13 @@ describe HarvestsController do it "destroys the requested harvest" do harvest = Harvest.create! valid_attributes expect do - delete :destroy, id: harvest.to_param + delete :destroy, params: { id: harvest.to_param } end.to change(Harvest, :count).by(-1) end it "redirects to the harvests list" do harvest = Harvest.create! valid_attributes - delete :destroy, id: harvest.to_param + delete :destroy, params: { id: harvest.to_param } response.should redirect_to(harvests_url) end end diff --git a/spec/controllers/likes_controller_spec.rb b/spec/controllers/likes_controller_spec.rb index 6f43760c1..59e0ddd89 100644 --- a/spec/controllers/likes_controller_spec.rb +++ b/spec/controllers/likes_controller_spec.rb @@ -10,7 +10,7 @@ describe LikesController do describe "POST create" do it { expect(response.content_type).to eq "application/json" } - before { post :create, post_id: blogpost.id, format: :json } + before { post :create, params: { post_id: blogpost.id, format: :json } } it { expect(Like.last.likeable_id).to eq(blogpost.id) } it { expect(Like.last.likeable_type).to eq('Post') } it { JSON.parse(response.body)["description"] == "1 like" } @@ -25,7 +25,7 @@ describe LikesController do end describe "DELETE destroy" do - before { delete :destroy, id: like.id, format: :json } + before { delete :destroy, params: { id: like.id, format: :json } } it { expect(response.content_type).to eq "application/json" } describe "un-liking something i liked before" do diff --git a/spec/controllers/member_controller_spec.rb b/spec/controllers/member_controller_spec.rb index b04bcd799..dada0b013 100644 --- a/spec/controllers/member_controller_spec.rb +++ b/spec/controllers/member_controller_spec.rb @@ -10,7 +10,7 @@ describe MembersController do describe "GET index" do it "assigns only confirmed members as @members" do - get :index, {} + get :index, params: {} assigns(:members).should eq([@member]) end end @@ -24,38 +24,38 @@ describe MembersController do describe "GET show" do it "provides JSON for member profile" do - get :show, id: @member.id, format: 'json' + get :show, params: { id: @member.id }, format: 'json' response.should be_success end it "assigns @posts with the member's posts" do - get :show, id: @member.id + get :show, params: { id: @member.id } assigns(:posts).should eq(@posts) end it "assigns @twitter_auth" do - get :show, id: @member.id + get :show, params: { id: @member.id } assigns(:twitter_auth).should eq(@twitter_auth) end it "assigns @flickr_auth" do - get :show, id: @member.id + get :show, params: { id: @member.id } assigns(:flickr_auth).should eq(@flickr_auth) end it "doesn't show completely nonsense members" do - lambda { get :show, id: 9999 }.should raise_error(ActiveRecord::RecordNotFound) + lambda { get :show, params: { id: 9999 } }.should raise_error(ActiveRecord::RecordNotFound) end it "doesn't show unconfirmed members" do @member2 = FactoryBot.create(:unconfirmed_member) - lambda { get :show, id: @member2.id }.should raise_error(ActiveRecord::RecordNotFound) + lambda { get :show, params: { id: @member2.id } }.should raise_error(ActiveRecord::RecordNotFound) end end describe "GET member's RSS feed" do it "returns an RSS feed" do - get :show, id: @member.to_param, format: "rss" + get :show, params: { id: @member.to_param }, format: "rss" response.should be_success response.should render_template("members/show") response.content_type.should eq("application/rss+xml") diff --git a/spec/controllers/notifications_controller_spec.rb b/spec/controllers/notifications_controller_spec.rb index fd6f40aa1..a7ed81ae5 100644 --- a/spec/controllers/notifications_controller_spec.rb +++ b/spec/controllers/notifications_controller_spec.rb @@ -31,7 +31,7 @@ describe NotificationsController do describe "GET index" do it "assigns all notifications as @notifications" do notification = FactoryBot.create(:notification, recipient_id: subject.current_member.id) - get :index, {} + get :index, params: {} assigns(:notifications).should eq([notification]) end end @@ -39,14 +39,14 @@ describe NotificationsController do describe "GET show" do it "assigns the requested notification as @notification" do notification = FactoryBot.create(:notification, recipient_id: subject.current_member.id) - get :show, id: notification.to_param + get :show, params: { id: notification.to_param } assigns(:notification).should eq(notification) end it "assigns the reply link for a post comment" do notification = FactoryBot.create(:notification, recipient_id: subject.current_member.id) - get :show, id: notification.to_param + get :show, params: { id: notification.to_param } assigns(:reply_link).should_not be_nil assigns(:reply_link).should eq new_comment_url( post_id: notification.post.id @@ -55,7 +55,7 @@ describe NotificationsController do it "marks notifications as read" do notification = FactoryBot.create(:notification, recipient_id: subject.current_member.id) - get :show, id: notification.to_param + get :show, params: { id: notification.to_param } # we need to fetch it from the db again, can't test against the old one n = Notification.find(notification.id) n.read.should eq true @@ -65,7 +65,7 @@ describe NotificationsController do describe "GET reply" do it "marks notifications as read" do notification = FactoryBot.create(:notification, recipient_id: subject.current_member.id) - get :reply, id: notification.to_param + get :reply, params: { id: notification.to_param } # we need to fetch it from the db again, can't test against the old one n = Notification.find(notification.id) n.read.should eq true @@ -75,7 +75,7 @@ describe NotificationsController do describe "GET new" do it "assigns a recipient" do @recipient = FactoryBot.create(:member) - get :new, recipient_id: @recipient.id + get :new, params: { recipient_id: @recipient.id } assigns(:recipient).should be_an_instance_of(Member) end end @@ -84,7 +84,7 @@ describe NotificationsController do describe "with valid params" do it "redirects to the recipient's profile" do @recipient = FactoryBot.create(:member) - post :create, notification: { recipient_id: @recipient.id, subject: 'foo' } + post :create, params: { notification: { recipient_id: @recipient.id, subject: 'foo' } } response.should redirect_to(notifications_path) end end diff --git a/spec/controllers/order_items_controller_spec.rb b/spec/controllers/order_items_controller_spec.rb index 4de8eb98d..9064a0f2a 100644 --- a/spec/controllers/order_items_controller_spec.rb +++ b/spec/controllers/order_items_controller_spec.rb @@ -19,7 +19,9 @@ describe OrderItemsController do describe "POST create" do describe "redirects to order" do before do - post :create, order_item: { order_id: order.id, product_id: product.id, price: product.min_price } + post :create, params: { + order_item: { order_id: order.id, product_id: product.id, price: product.min_price } + } end it { expect(response).to redirect_to(OrderItem.last.order) } it { expect(OrderItem.last.order).to be_an_instance_of Order } @@ -28,9 +30,11 @@ describe OrderItemsController do describe 'creates an order for you' do it do expect do - post :create, order_item: { - product_id: product.id, - price: product.min_price + post :create, params: { + order_item: { + product_id: product.id, + price: product.min_price + } } end.to change(Order, :count).by(1) end @@ -41,7 +45,9 @@ describe OrderItemsController do order = FactoryBot.create(:order, member: member) product = FactoryBot.create(:product, min_price: 1) expect do - post :create, order_item: { order_id: order.id, product_id: product.id, price: 3.33 } + post :create, params: { + order_item: { order_id: order.id, product_id: product.id, price: 3.33 } + } end.to change(OrderItem, :count).by(1) OrderItem.last.price.should eq 333 end diff --git a/spec/controllers/orders_controller_spec.rb b/spec/controllers/orders_controller_spec.rb index 3c6631d8c..a36365aad 100644 --- a/spec/controllers/orders_controller_spec.rb +++ b/spec/controllers/orders_controller_spec.rb @@ -16,7 +16,7 @@ describe OrdersController do member = FactoryBot.create(:member) sign_in member order = Order.create!(member_id: member.id) - get :checkout, id: order.to_param, referral_code: 'FOOBAR' + get :checkout, params: { id: order.to_param, referral_code: 'FOOBAR' } order.reload order.referral_code.should eq 'FOOBAR' end @@ -25,7 +25,7 @@ describe OrdersController do member = FactoryBot.create(:member) sign_in member order = Order.create!(member_id: member.id) - get :checkout, id: order.to_param + get :checkout, params: { id: order.to_param } response.status.should eq 302 response.redirect_url.should match(/paypal\.com/) end @@ -36,7 +36,7 @@ describe OrdersController do member = FactoryBot.create(:member) sign_in member order = Order.create!(member_id: member.id) - get :complete, id: order.to_param + get :complete, params: { id: order.to_param } assigns(:order).should eq(order) end end @@ -46,7 +46,7 @@ describe OrdersController do member = FactoryBot.create(:member) sign_in member order = Order.create!(member_id: member.id) - delete :destroy, id: order.id + delete :destroy, params: { id: order.id } response.should redirect_to(shop_url) end end diff --git a/spec/controllers/photo_associations_controller_spec.rb b/spec/controllers/photo_associations_controller_spec.rb index 42e0ccc37..65a0dd37d 100644 --- a/spec/controllers/photo_associations_controller_spec.rb +++ b/spec/controllers/photo_associations_controller_spec.rb @@ -19,7 +19,7 @@ describe PhotoAssociationsController do let(:photo) { FactoryBot.create :photo, owner: member } it "removes link" do - expect { delete :destroy, valid_params }.to change { photo.harvests.count }.by(-1) + expect { delete :destroy, params: valid_params }.to change { photo.harvests.count }.by(-1) end end @@ -29,13 +29,13 @@ describe PhotoAssociationsController do it do expect do begin - delete :destroy, valid_params + delete :destroy, params: valid_params rescue nil end end.not_to change(photo.harvests, :count) end - it { expect { delete :destroy, valid_params }.to raise_error(ActiveRecord::RecordNotFound) } + it { expect { delete :destroy, params: valid_params }.to raise_error(ActiveRecord::RecordNotFound) } end end end diff --git a/spec/controllers/photos_controller_spec.rb b/spec/controllers/photos_controller_spec.rb index 3c4f9dacb..b001141ab 100644 --- a/spec/controllers/photos_controller_spec.rb +++ b/spec/controllers/photos_controller_spec.rb @@ -38,7 +38,7 @@ describe PhotosController do end describe "planting photos" do - before(:each) { get :new, type: "planting", id: planting.id } + before(:each) { get :new, params: { type: "planting", id: planting.id } } it { assigns(:flickr_auth).should be_an_instance_of(Authentication) } it { assigns(:item).should eq planting } it { expect(flash[:alert]).not_to be_present } @@ -46,7 +46,7 @@ describe PhotosController do end describe "harvest photos" do - before { get :new, type: "harvest", id: harvest.id } + before { get :new, params: { type: "harvest", id: harvest.id } } it { assigns(:item).should eq harvest } it { expect(flash[:alert]).not_to be_present } end @@ -76,15 +76,22 @@ describe PhotosController do describe "with valid params" do before { controller.stub(:current_member) { member } } it "attaches the photo to a planting" do - post :create, photo: { flickr_photo_id: photo.flickr_photo_id }, type: "planting", id: planting.id + post :create, params: { + photo: { flickr_photo_id: photo.flickr_photo_id }, + type: "planting", id: planting.id + } expect(flash[:alert]).not_to be_present Photo.last.plantings.first.should eq planting end describe "doesn't attach a photo to a planting twice" do before do - post :create, photo: { flickr_photo_id: photo.flickr_photo_id }, type: "planting", id: planting.id - post :create, photo: { flickr_photo_id: photo.flickr_photo_id }, type: "planting", id: planting.id + post :create, params: { + photo: { flickr_photo_id: photo.flickr_photo_id }, type: "planting", id: planting.id + } + post :create, params: { + photo: { flickr_photo_id: photo.flickr_photo_id }, type: "planting", id: planting.id + } end it { expect(flash[:alert]).not_to be_present } it { expect(Photo.last.plantings.size).to eq 1 } @@ -97,8 +104,12 @@ describe PhotosController do end it "doesn't attach a photo to a harvest twice" do - post :create, photo: { flickr_photo_id: photo.flickr_photo_id }, type: "harvest", id: harvest.id - post :create, photo: { flickr_photo_id: photo.flickr_photo_id }, type: "harvest", id: harvest.id + post :create, params: { + photo: { flickr_photo_id: photo.flickr_photo_id }, type: "harvest", id: harvest.id + } + post :create, params: { + photo: { flickr_photo_id: photo.flickr_photo_id }, type: "harvest", id: harvest.id + } expect(flash[:alert]).not_to be_present Photo.last.harvests.size.should eq 1 end @@ -106,20 +117,19 @@ describe PhotosController do it "doesn't attach photo to a comment" do comment = FactoryBot.create(:comment) expect do - post :create, photo: { flickr_photo_id: photo.flickr_photo_id }, type: "comment", id: comment.id + post :create, params: { + photo: { flickr_photo_id: photo.flickr_photo_id }, type: "comment", id: comment.id + } end.to raise_error end end describe "for the second time" do let(:planting) { FactoryBot.create :planting, owner: member } + let(:valid_params) { { photo: { flickr_photo_id: 1 }, id: planting.id, type: 'planting' } } it "does not add a photo twice" do - expect do - post :create, photo: { flickr_photo_id: 1 }, id: planting.id, type: 'planting' - end.to change(Photo, :count).by(1) - expect do - post :create, photo: { flickr_photo_id: 1 }, id: planting.id, type: 'planting' - end.to change(Photo, :count).by(0) + expect { post :create, params: valid_params }.to change(Photo, :count).by(1) + expect { post :create, params: valid_params }.not_to change(Photo, :count) end end @@ -128,15 +138,15 @@ describe PhotosController do it "creates the planting/photo link" do planting = FactoryBot.create(:planting, garden: garden, owner: member) photo = FactoryBot.create(:photo, owner: member) - post :create, photo: { flickr_photo_id: photo.flickr_photo_id }, type: "planting", id: planting.id + post :create, params: { photo: { flickr_photo_id: photo.flickr_photo_id }, type: "planting", id: planting.id } expect(flash[:alert]).not_to be_present - Photo.last.plantings.first.should eq planting + expect(Photo.last.plantings.first).to eq planting end - it "creates the harvest/photo link" do - post :create, photo: { flickr_photo_id: photo.flickr_photo_id }, type: "harvest", id: harvest.id - expect(flash[:alert]).not_to be_present - Photo.last.harvests.first.should eq harvest + describe "creates the harvest/photo link" do + before { post :create, photo: { flickr_photo_id: photo.flickr_photo_id }, type: "harvest", id: harvest.id } + it { expect(flash[:alert]).not_to be_present } + it { expect(Photo.last.harvests.first).to eq harvest } end end @@ -146,7 +156,9 @@ describe PhotosController do # members will be auto-created, and different another_planting = FactoryBot.create(:planting) expect do - post :create, photo: { flickr_photo_id: photo.flickr_photo_id }, type: "planting", id: another_planting.id + post :create, params: { + photo: { flickr_photo_id: photo.flickr_photo_id }, type: "planting", id: another_planting.id + } end.to raise_error(ActiveRecord::RecordNotFound) Photo.last.plantings.first.should_not eq another_planting end @@ -155,7 +167,9 @@ describe PhotosController do # members will be auto-created, and different another_harvest = FactoryBot.create(:harvest) expect do - post :create, photo: { flickr_photo_id: photo.flickr_photo_id }, type: "harvest", id: another_harvest.id + post :create, params: { + photo: { flickr_photo_id: photo.flickr_photo_id }, type: "harvest", id: another_harvest.id + } end.to raise_error(ActiveRecord::RecordNotFound) Photo.last.harvests.first.should_not eq another_harvest end diff --git a/spec/controllers/places_controller_spec.rb b/spec/controllers/places_controller_spec.rb index 8b6cb0a31..d235b98c9 100644 --- a/spec/controllers/places_controller_spec.rb +++ b/spec/controllers/places_controller_spec.rb @@ -12,19 +12,19 @@ describe PlacesController do end it "assigns place name" do - get :show, place: @member_london.location + get :show, params: { place: @member_london.location } assigns(:place).should eq @member_london.location end it "assigns nearby members" do - get :show, place: @member_london.location + get :show, params: { place: @member_london.location } assigns(:nearby_members).should eq [@member_london, @member_south_pole] end end describe "GET search" do it "redirects to the new place" do - get :search, new_place: "foo" + get :search, params: { new_place: "foo" } response.should redirect_to place_path("foo") end end diff --git a/spec/controllers/plantings_controller_spec.rb b/spec/controllers/plantings_controller_spec.rb index dd1340d44..c1e4cd72b 100644 --- a/spec/controllers/plantings_controller_spec.rb +++ b/spec/controllers/plantings_controller_spec.rb @@ -24,13 +24,13 @@ describe PlantingsController do end describe "picks up owner from params and shows owner's plantings only" do - before { get :index, owner: member1.slug } + before { get :index, params: { owner: member1.slug } } it { expect(assigns(:owner)).to eq member1 } it { expect(assigns(:plantings)).to eq [planting1] } end describe "picks up crop from params and shows the plantings for the crop only" do - before { get :index, crop: maize.name } + before { get :index, params: { crop: maize.name } } it { expect(assigns(:crop)).to eq maize } it { expect(assigns(:plantings)).to eq [planting2] } end @@ -39,44 +39,44 @@ describe PlantingsController do describe "GET new" do describe "picks up crop from params" do let(:crop) { FactoryBot.create(:crop) } - before { get :new, crop_id: crop.id } + before { get :new, params: { crop_id: crop.id } } it { expect(assigns(:crop)).to eq(crop) } end describe "doesn't die if no crop specified" do - before { get :new, {} } + before { get :new, params: {} } it { expect(assigns(:crop)).to be_a_new(Crop) } end describe "picks up member's garden from params" do let(:garden) { FactoryBot.create(:garden, owner: member) } - before { get :new, garden_id: garden.id } + before { get :new, params: { garden_id: garden.id } } it { expect(assigns(:garden)).to eq(garden) } end describe "Doesn't display another member's garden on planting form" do let(:another_member) { FactoryBot.create(:member) } # over-riding member from login_member() let(:garden) { FactoryBot.create(:garden, owner: another_member) } - before { get :new, garden_id: garden.id } + before { get :new, params: { garden_id: garden.id } } it { expect(assigns(:garden)).not_to eq(garden) } end describe "Doesn't display un-approved crops on planting form" do let(:crop) { FactoryBot.create(:crop, approval_status: 'pending') } let!(:garden) { FactoryBot.create(:garden, owner: member) } - before { get :new, crop_id: crop.id } + before { get :new, params: { crop_id: crop.id } } it { expect(assigns(:crop)).not_to eq(crop) } end describe "Doesn't display rejected crops on planting form" do let(:crop) { FactoryBot.create(:crop, approval_status: 'rejected', reason_for_rejection: 'nope') } let!(:garden) { FactoryBot.create(:garden, owner: member) } - before { get :new, crop_id: crop.id } + before { get :new, params: { crop_id: crop.id } } it { expect(assigns(:crop)).not_to eq(crop) } end describe "doesn't die if no garden specified" do - before { get :new, {} } + before { get :new, params: {} } it { expect(assigns(:garden)).to be_a_new(Garden) } end @@ -86,7 +86,7 @@ describe PlantingsController do end describe "sets the owner automatically" do - before { post :create, planting: valid_attributes } + before { post :create, params: { planting: valid_attributes } } it { expect(assigns(:planting).owner).to eq subject.current_member } end end diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb index cbfe8b46c..2d7aba1ad 100644 --- a/spec/controllers/posts_controller_spec.rb +++ b/spec/controllers/posts_controller_spec.rb @@ -11,19 +11,19 @@ describe PostsController do describe "GET RSS feed" do it "returns an RSS feed" do get :index, format: "rss" - response.should be_success - response.should render_template("posts/index") - response.content_type.should eq("application/rss+xml") + expect(response).to be_success + expect(response).to render_template("posts/index") + expect(response).to.should eq("application/rss+xml") end end describe "GET RSS feed for individual post" do it "returns an RSS feed" do post = Post.create! valid_attributes - get :show, format: "rss", id: post.slug - response.should be_success - response.should render_template("posts/show") - response.content_type.should eq("application/rss+xml") + get :show, format: "rss", params: { id: post.slug } + expect(response).to be_success + expect(response).to render_template("posts/show") + expect(response).to.should eq("application/rss+xml") end end end diff --git a/spec/controllers/roles_controller_spec.rb b/spec/controllers/roles_controller_spec.rb index 9bf0ea1b5..17fe48dfe 100644 --- a/spec/controllers/roles_controller_spec.rb +++ b/spec/controllers/roles_controller_spec.rb @@ -10,7 +10,7 @@ describe RolesController do describe "GET index" do it "assigns all roles as @roles" do role = Role.create! valid_attributes - get :index, {} + get :index, params: {} # note that admin role exists because of login_admin_member assigns(:roles).should eq([Role.find_by(name: 'admin'), role]) end diff --git a/spec/controllers/scientific_names_controller_spec.rb b/spec/controllers/scientific_names_controller_spec.rb index 412f53224..a56735454 100644 --- a/spec/controllers/scientific_names_controller_spec.rb +++ b/spec/controllers/scientific_names_controller_spec.rb @@ -3,17 +3,15 @@ require 'rails_helper' describe ScientificNamesController do login_member(:crop_wrangling_member) - before(:each) do - @crop = FactoryBot.create(:tomato) - end + let!(:crop) { FactoryBot.create(:tomato) } def valid_attributes - { name: 'Solanum lycopersicum', crop_id: @crop.id } + { name: 'Solanum lycopersicum', crop_id: crop.id } end describe "GET new" do it "assigns crop if specified" do - get :new, crop_id: 1 + get :new, params: { crop_id: crop.id } assigns(:crop).should be_an_instance_of Crop end end diff --git a/spec/controllers/seeds_controller_spec.rb b/spec/controllers/seeds_controller_spec.rb index c2fd699eb..123516e43 100644 --- a/spec/controllers/seeds_controller_spec.rb +++ b/spec/controllers/seeds_controller_spec.rb @@ -2,10 +2,10 @@ require 'rails_helper' describe SeedsController do describe "GET index" do - it "picks up owner from params" do - owner = FactoryBot.create(:member) - get :index, owner: owner.slug - assigns(:owner).should eq(owner) + let(:owner) { FactoryBot.create(:member) } + describe "picks up owner from params" do + before { get :index, params: { owner: owner.slug } } + it { expect(assigns(:owner)).to eq(owner) } end end end diff --git a/spec/controllers/shop_controller_spec.rb b/spec/controllers/shop_controller_spec.rb index c25039d2d..c3fb1e3c0 100644 --- a/spec/controllers/shop_controller_spec.rb +++ b/spec/controllers/shop_controller_spec.rb @@ -6,7 +6,7 @@ describe ShopController do describe "GET index" do describe 'not logged in' do - before { get :index, {} } + before { get :index, params: {} } describe "assigns all products as @products ordered by name" do it { expect(assigns(:products)).to eq([product1, product2]) } @@ -26,7 +26,7 @@ describe ShopController do let!(:order) { FactoryBot.create(:order, member: member) } before do sign_in member - get :index, {} + get :index, params: {} end it { expect(assigns(:order)).to eq order } end From 2b69db77bf30e2dbfc37139a979a00a615bfc623 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Fri, 5 Jan 2018 21:33:21 +1300 Subject: [PATCH 038/219] Use faker gem for Member factorybot --- spec/factories/member.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/factories/member.rb b/spec/factories/member.rb index 619e7d4f2..c437ff1bb 100644 --- a/spec/factories/member.rb +++ b/spec/factories/member.rb @@ -3,9 +3,9 @@ FactoryBot.define do sequence(:login_name) { |n| "member#{n}" } factory :member, aliases: %i(author owner sender recipient creator) do - login_name { generate(:login_name) } + login_name { Faker::Name.unique.first_name } password 'password1' - email { generate(:email) } + email { Faker::Internet.unique.email } tos_agreement true confirmed_at Time.now show_email false From 4e5aa8077c94c66122a0a59719250a672f48b679 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Fri, 5 Jan 2018 21:34:11 +1300 Subject: [PATCH 039/219] Re-wrote harvests#update controller spec --- spec/controllers/harvests_controller_spec.rb | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/spec/controllers/harvests_controller_spec.rb b/spec/controllers/harvests_controller_spec.rb index fca0bb840..91f441443 100644 --- a/spec/controllers/harvests_controller_spec.rb +++ b/spec/controllers/harvests_controller_spec.rb @@ -129,13 +129,12 @@ describe HarvestsController do describe "PUT update" do describe "with valid params" do it "updates the requested harvest" do - harvest = Harvest.create! valid_attributes - # Assuming there are no other harvests in the database, this - # specifies that the Harvest created on the previous line - # receives the :update message with whatever params are - # submitted in the request. - Harvest.any_instance.should_receive(:update).with("crop_id" => "1", "owner_id": member.id) - put :update, params: { id: harvest.to_param, harvest: { "crop_id" => "1" } } + harvest = FactoryBot.create :harvest, valid_attributes + new_crop = FactoryBot.create :crop + expect do + put :update, params: { id: harvest.to_param, harvest: { crop_id: new_crop.id } } + harvest.reload + end.to change(harvest, :crop_id).to(new_crop.id) end it "assigns the requested harvest as @harvest" do From a0cc9c0a00424eb6c6a53c7725198cb2a8ad15a0 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 08:41:34 +1300 Subject: [PATCH 040/219] fixed photo controller specs --- spec/controllers/photos_controller_spec.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spec/controllers/photos_controller_spec.rb b/spec/controllers/photos_controller_spec.rb index b001141ab..1b2bb6fb1 100644 --- a/spec/controllers/photos_controller_spec.rb +++ b/spec/controllers/photos_controller_spec.rb @@ -52,8 +52,8 @@ describe PhotosController do end describe "garden photos" do - before { get :new, type: "garden", id: garden.id } - it { assigns(:item).should eq garden } + before { get :new, params: { type: "garden", id: garden.id } } + it { expect(assigns(:item)).to eq garden } it { expect(flash[:alert]).not_to be_present } end end @@ -98,7 +98,7 @@ describe PhotosController do end it "attaches the photo to a harvest" do - post :create, photo: { flickr_photo_id: photo.flickr_photo_id }, type: "harvest", id: harvest.id + post :create, params: { photo: { flickr_photo_id: photo.flickr_photo_id }, type: "harvest", id: harvest.id } expect(flash[:alert]).not_to be_present Photo.last.harvests.first.should eq harvest end @@ -144,7 +144,9 @@ describe PhotosController do end describe "creates the harvest/photo link" do - before { post :create, photo: { flickr_photo_id: photo.flickr_photo_id }, type: "harvest", id: harvest.id } + before do + post :create, params: { photo: { flickr_photo_id: photo.flickr_photo_id }, type: "harvest", id: harvest.id } + end it { expect(flash[:alert]).not_to be_present } it { expect(Photo.last.harvests.first).to eq harvest } end From 462654f26e5846b3b7dd75fccdd1d86314a3a0b1 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 08:51:11 +1300 Subject: [PATCH 041/219] Fixed gardens controller specs --- spec/controllers/gardens_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/controllers/gardens_controller_spec.rb b/spec/controllers/gardens_controller_spec.rb index f661a7299..bdbc754ed 100644 --- a/spec/controllers/gardens_controller_spec.rb +++ b/spec/controllers/gardens_controller_spec.rb @@ -11,7 +11,7 @@ RSpec.describe GardensController, type: :controller do it { expect(response).to redirect_to(new_member_session_path) } end describe 'PUT create' do - before { put :create, garden: valid_params } + before { put :create, params: { garden: valid_params } } it { expect(response).to redirect_to(new_member_session_path) } end From a2190dc9847f1984c6ff478b677f8a6bba78e897 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 08:54:44 +1300 Subject: [PATCH 042/219] Fixed a matcher in posts controller spec --- spec/controllers/posts_controller_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb index 2d7aba1ad..2dde4f8ae 100644 --- a/spec/controllers/posts_controller_spec.rb +++ b/spec/controllers/posts_controller_spec.rb @@ -13,7 +13,7 @@ describe PostsController do get :index, format: "rss" expect(response).to be_success expect(response).to render_template("posts/index") - expect(response).to.should eq("application/rss+xml") + expect(response.content_type).to eq("application/rss+xml") end end @@ -23,7 +23,7 @@ describe PostsController do get :show, format: "rss", params: { id: post.slug } expect(response).to be_success expect(response).to render_template("posts/show") - expect(response).to.should eq("application/rss+xml") + expect(response.content_type).to eq("application/rss+xml") end end end From 62fd16e6b222f7a068fae5c7c536bbad2463dbbd Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 09:01:18 +1300 Subject: [PATCH 043/219] Notifications don't have a post if they are a message --- app/models/notification.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/notification.rb b/app/models/notification.rb index fe3ec8907..c9f915420 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -1,7 +1,7 @@ class Notification < ApplicationRecord belongs_to :sender, class_name: 'Member' belongs_to :recipient, class_name: 'Member' - belongs_to :post + belongs_to :post, optional: true validates :subject, length: { maximum: 255 } From 19382a8a36b4164cfb9149c535dcdad46a78a75a Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 09:23:34 +1300 Subject: [PATCH 044/219] Random login name generator was running out of names So here's a name plus a random number instead It might collide sometimes alas --- spec/factories/member.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/factories/member.rb b/spec/factories/member.rb index c437ff1bb..1e986d9b2 100644 --- a/spec/factories/member.rb +++ b/spec/factories/member.rb @@ -3,7 +3,7 @@ FactoryBot.define do sequence(:login_name) { |n| "member#{n}" } factory :member, aliases: %i(author owner sender recipient creator) do - login_name { Faker::Name.unique.first_name } + login_name { "#{Faker::Name.first_name}_#{rand(1..1000)}" } password 'password1' email { Faker::Internet.unique.email } tos_agreement true From a0fe19651edadfbba67b8136cf9e53e2cec9ce39 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 09:31:39 +1300 Subject: [PATCH 045/219] Plant parts model using distinct query --- app/models/plant_part.rb | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/app/models/plant_part.rb b/app/models/plant_part.rb index 2590fb966..bbdbae8d2 100644 --- a/app/models/plant_part.rb +++ b/app/models/plant_part.rb @@ -3,21 +3,9 @@ class PlantPart < ApplicationRecord friendly_id :name, use: %i(slugged finders) has_many :harvests - has_many :crops, -> { uniq }, through: :harvests + has_many :crops, -> { distinct }, through: :harvests def to_s name end - - # Postgres complains if the ORDER BY clause of a SELECT DISTINCT query is - # not precisely one of the SELECTed fields. The default sort order on - # crops is lower(name), and Postgres is not smart enough to notice that it - # can calculate this from fields which are selected. The solution is to - # override PlantParts#crops to remove the ORDER BY clause, and replace it - # with `ORDER BY name`. This is not perfect, because it means the crops - # associated to plant parts will not be sorted in the same order as crops - # on the rest of the site. - def crops - super.reorder('name') - end end From fcbb57e6591f67c07a397eef3758fcc8fd112037 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 10:47:04 +1300 Subject: [PATCH 046/219] Respond with data on gardens#edit, so non-html formats don't break specs --- app/controllers/gardens_controller.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/gardens_controller.rb b/app/controllers/gardens_controller.rb index ede5c2602..35efaf5d2 100644 --- a/app/controllers/gardens_controller.rb +++ b/app/controllers/gardens_controller.rb @@ -33,7 +33,9 @@ class GardensController < ApplicationController end # GET /gardens/1/edit - def edit; end + def edit + respond_with(@garden) + end # POST /gardens # POST /gardens.json From 7bb49f337f66a8c159822f651ec95eaf6a942c07 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 11:31:04 +1300 Subject: [PATCH 047/219] Fixing up member's account model creation, post rails 5 upgrade --- app/models/account.rb | 7 ++++--- app/models/member.rb | 19 +++++++++++++++---- spec/models/member_spec.rb | 32 +++++++++++++++----------------- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/app/models/account.rb b/app/models/account.rb index 79d5327b7..be5b1e9cf 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -6,9 +6,10 @@ class Account < ApplicationRecord message: 'already has account details associated with it' } - before_create do |account| - unless account.account_type - account.account_type = AccountType.find_or_create_by(name: + before_validation do + # If not account type, set to the free account + unless account_type + self.account_type = AccountType.find_or_create_by(name: Rails.application.config.default_account_type) end end diff --git a/app/models/member.rb b/app/models/member.rb index 654e6b3bd..b22561ed9 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -77,13 +77,16 @@ class Member < ApplicationRecord after_validation :geocode after_validation :empty_unwanted_geocodes after_save :update_newsletter_subscription + after_create :create_account_and_garden # Give each new member a default garden # and an account record (for paid accounts etc) # we use find_or_create to avoid accidentally creating a second one, # which can happen sometimes especially with FactoryBot associations - after_create { |member| Garden.create(name: "Garden", owner_id: member.id) } - after_create { |member| Account.find_or_create_by(member_id: member.id) } + def create_account_and_garden + Garden.create!(name: "Garden", owner_id: id) + Account.find_or_create_by!(member_id: id) + end # allow login via either login_name or email address def self.find_first_by_auth_conditions(warden_conditions) @@ -198,15 +201,23 @@ class Member < ApplicationRecord end def update_newsletter_subscription - return unless confirmed_at_changed? || newsletter_changed? + return unless will_save_change_to_attribute?(:confirmed) || will_save_change_to_attribute?(:newsletter) if newsletter - newsletter_subscribe if confirmed_at_changed? || confirmed_at && newsletter_changed? + newsletter_subscribe if confirmed_just_now? || requested_newsletter_just_now? elsif confirmed_at newsletter_unsubscribe end end + def confirmed_just_now? + will_save_change_to_attribute?(:confirmed_at) + end + + def requested_newsletter_just_now? + confirmed_at && will_save_change_to_attribute?(:newsletter) + end + def newsletter_subscribe(gb = Gibbon::API.new, testing = false) return true if Rails.env.test? && !testing gb.lists.subscribe( diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index c538ad38d..9cf80597b 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -2,53 +2,51 @@ require 'rails_helper' describe 'member' do context 'valid member' do - let(:member) { FactoryBot.create(:member) } + let!(:member) { FactoryBot.create(:member, login_name: 'hinemoa') } - it 'should be fetchable from the database' do - member2 = Member.find(member.id) - member2.should be_an_instance_of Member - member2.login_name.should match(/member\d+/) - member2.encrypted_password.should_not be_nil + describe 'should be fetchable from the database' do + subject { Member.find(member.id) } + it { is_expected.to be_an_instance_of Member } + it { expect(subject.encrypted_password).not_to be_nil } end - it 'should have a friendly slug' do - member.slug.should match(/member\d+/) + describe 'should have a friendly slug' do + it { expect(member.slug).to eq('hinemoa') } end it 'has a bio' do member.bio = 'I love seeds' - member.bio.should eq 'I love seeds' + expect(member.bio).to eq 'I love seeds' end it 'should have a default garden' do - member.gardens.size.should == 1 + expect(member.gardens.count).to eq 1 end it 'should have a accounts entry' do - member.account.should be_an_instance_of Account + expect(member.account).to be_an_instance_of Account end it "should have a default-type account by default" do member.account.account_type.name.should eq Rails.application.config.default_account_type - member.paid?.should be(false) + expect(member.paid?).to be_false end it "doesn't show email by default" do - member.show_email.should be(false) + expect(member.show_email).to be_false end it 'should stringify as the login_name' do - member.to_s.should match(/member\d+/) - member.to_s.should match(/member\d+/) + expect(member.to_s).to eq 'hinemoa' end it 'should be able to fetch posts' do post = FactoryBot.create(:post, author: member) - member.posts.should eq [post] + expect(member.posts).to eq [post] end it 'should be able to fetch gardens' do - member.gardens.first.name.should eq "Garden" + expect(member.gardens.first.name).to eq "Garden" end it 'has many plantings' do From e5beb066a049872873fe44e11f8c772790fdf8af Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 11:32:49 +1300 Subject: [PATCH 048/219] Moved members.create_account_and_garden to private --- app/models/member.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/models/member.rb b/app/models/member.rb index b22561ed9..7165faf4f 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -79,15 +79,6 @@ class Member < ApplicationRecord after_save :update_newsletter_subscription after_create :create_account_and_garden - # Give each new member a default garden - # and an account record (for paid accounts etc) - # we use find_or_create to avoid accidentally creating a second one, - # which can happen sometimes especially with FactoryBot associations - def create_account_and_garden - Garden.create!(name: "Garden", owner_id: id) - Account.find_or_create_by!(member_id: id) - end - # allow login via either login_name or email address def self.find_first_by_auth_conditions(warden_conditions) conditions = warden_conditions.dup @@ -241,4 +232,13 @@ class Member < ApplicationRecord def get_follow(member) follows.find_by(followed_id: member.id) if already_following?(member) end + + # Give each new member a default garden + # and an account record (for paid accounts etc) + # we use find_or_create to avoid accidentally creating a second one, + # which can happen sometimes especially with FactoryBot associations + def create_account_and_garden + Garden.create!(name: "Garden", owner_id: id) + Account.find_or_create_by!(member_id: id) + end end From 5eae80bc6d295b7f6e4ba4b98d61a2c1e3d47688 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 11:38:47 +1300 Subject: [PATCH 049/219] Specify login name in spec, so we know what to look for Becuase we're using the faker gem now --- spec/models/garden_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/garden_spec.rb b/spec/models/garden_spec.rb index f8e5a3311..3ee483c04 100644 --- a/spec/models/garden_spec.rb +++ b/spec/models/garden_spec.rb @@ -1,11 +1,11 @@ require 'rails_helper' describe Garden do - let(:owner) { FactoryBot.create(:member) } + let(:owner) { FactoryBot.create(:member, login_name: 'hatupatu') } let(:garden) { FactoryBot.create(:garden, owner: owner) } it "should have a slug" do - garden.slug.should match(/member\d+-springfield-community-garden/) + garden.slug.should match(/hatupatu-springfield-community-garden/) end it "should have a description" do From d642e7cf40bd9ab59ffdf800dd20bfe4aa2a67f6 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 11:48:45 +1300 Subject: [PATCH 050/219] set login name on member so we know what to look for in slug spec --- spec/models/planting_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/planting_spec.rb b/spec/models/planting_spec.rb index 48a035bb4..94fa1c291 100644 --- a/spec/models/planting_spec.rb +++ b/spec/models/planting_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' describe Planting do let(:crop) { FactoryBot.create(:tomato) } - let(:garden_owner) { FactoryBot.create(:member) } + let(:garden_owner) { FactoryBot.create(:member, login_name: 'hatupatu') } let(:garden) { FactoryBot.create(:garden, owner: garden_owner) } let(:planting) { FactoryBot.create(:planting, crop: crop, garden: garden, owner: garden.owner) } let(:finished_planting) do @@ -156,7 +156,7 @@ describe Planting do end it "should have a slug" do - planting.slug.should match(/^member\d+-springfield-community-garden-tomato$/) + planting.slug.should match(/^hatupatu-springfield-community-garden-tomato$/) end it 'should sort in reverse creation order' do From 22fd422a6df68a168e839303b122584a339afc7d Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 11:49:35 +1300 Subject: [PATCH 051/219] Look for eq false in member spec --- spec/models/member_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 9cf80597b..c0f7ac4f7 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -29,11 +29,11 @@ describe 'member' do it "should have a default-type account by default" do member.account.account_type.name.should eq Rails.application.config.default_account_type - expect(member.paid?).to be_false + expect(member.paid?).to eq false end it "doesn't show email by default" do - expect(member.show_email).to be_false + expect(member.show_email).to eq false end it 'should stringify as the login_name' do From dd980d3f4143909f16960b043946582ffe45bd02 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 12:06:26 +1300 Subject: [PATCH 052/219] Harvests#edit needs to respond with something --- app/controllers/harvests_controller.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/harvests_controller.rb b/app/controllers/harvests_controller.rb index 2e7fbef3e..55254516a 100644 --- a/app/controllers/harvests_controller.rb +++ b/app/controllers/harvests_controller.rb @@ -31,6 +31,7 @@ class HarvestsController < ApplicationController def edit @planting = @harvest.planting if @harvest.planting_id + respond_with(@harvest) end def create From c95630e249174f447b9b2aa38c948c904e0dcda6 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 12:19:42 +1300 Subject: [PATCH 053/219] Fixed api request specs after rails 5 upgrade --- spec/requests/api/v1/crop_request_spec.rb | 25 +++++++++------- spec/requests/api/v1/gardens_request_spec.rb | 25 +++++++++------- spec/requests/api/v1/harvest_request_spec.rb | 29 ++++++++++++------- spec/requests/api/v1/member_request_spec.rb | 28 +++++++++++------- spec/requests/api/v1/photos_request_spec.rb | 25 +++++++++------- .../requests/api/v1/plantings_request_spec.rb | 27 ++++++++++------- spec/requests/api/v1/seeds_request_spec.rb | 25 +++++++++------- 7 files changed, 107 insertions(+), 77 deletions(-) diff --git a/spec/requests/api/v1/crop_request_spec.rb b/spec/requests/api/v1/crop_request_spec.rb index cfac5fdb4..598a6993e 100644 --- a/spec/requests/api/v1/crop_request_spec.rb +++ b/spec/requests/api/v1/crop_request_spec.rb @@ -56,12 +56,12 @@ RSpec.describe 'Plantings', type: :request do subject { JSON.parse response.body } describe '#index' do - before { get '/api/v1/crops', {}, headers } + before { get '/api/v1/crops', params: {}, headers: headers } it { expect(subject['data']).to include(crop_encoded_as_json_api) } end describe '#show' do - before { get "/api/v1/crops/#{crop.id}", {}, headers } + before { get "/api/v1/crops/#{crop.id}", params: {}, headers: headers } it { expect(subject['data']['attributes']).to eq(attributes) } it { expect(subject['data']['relationships']).to include("plantings" => plantings_as_json_api) } it { expect(subject['data']['relationships']).to include("harvests" => harvests_as_json_api) } @@ -70,18 +70,21 @@ RSpec.describe 'Plantings', type: :request do it { expect(subject['data']).to eq(crop_encoded_as_json_api) } end - describe '#create' do - before { post '/api/v1/crops', { 'crop' => { 'name' => 'can i make this' } }, headers } - it { expect(response.code).to eq "404" } + it '#create' do + expect do + post '/api/v1/crops', params: { 'crop' => { 'name' => 'can i make this' } }, headers: headers + end.to raise_error ActionController::RoutingError end - describe '#update' do - before { post "/api/v1/crops/#{crop.id}", { 'crop' => { 'name' => 'can i modify this' } }, headers } - it { expect(response.code).to eq "404" } + it '#update' do + expect do + post "/api/v1/crops/#{crop.id}", params: { 'crop' => { 'name' => 'can i modify this' } }, headers: headers + end.to raise_error ActionController::RoutingError end - describe '#delete' do - before { delete "/api/v1/crops/#{crop.id}", {}, headers } - it { expect(response.code).to eq "404" } + it '#delete' do + expect do + delete "/api/v1/crops/#{crop.id}", params: {}, headers: headers + end.to raise_error ActionController::RoutingError end end diff --git a/spec/requests/api/v1/gardens_request_spec.rb b/spec/requests/api/v1/gardens_request_spec.rb index 65caf7044..ac1bb6786 100644 --- a/spec/requests/api/v1/gardens_request_spec.rb +++ b/spec/requests/api/v1/gardens_request_spec.rb @@ -38,27 +38,30 @@ RSpec.describe 'Gardens', type: :request do subject { JSON.parse response.body } scenario '#index' do - get '/api/v1/gardens', {}, headers + get '/api/v1/gardens', params: {}, headers: headers expect(subject['data']).to include(garden_encoded_as_json_api) end scenario '#show' do - get "/api/v1/gardens/#{garden.id}", {}, headers + get "/api/v1/gardens/#{garden.id}", params: {}, headers: headers expect(subject['data']).to include(garden_encoded_as_json_api) end - scenario '#create' do - post '/api/v1/gardens', { 'garden' => { 'name' => 'can i make this' } }, headers - expect(response.code).to eq "404" + it '#create' do + expect do + post '/api/v1/gardens', params: { 'garden' => { 'name' => 'can i make this' } }, headers: headers + end.to raise_error ActionController::RoutingError end - scenario '#update' do - post "/api/v1/gardens/#{garden.id}", { 'garden' => { 'name' => 'can i modify this' } }, headers - expect(response.code).to eq "404" + it '#update' do + expect do + post "/api/v1/gardens/#{garden.id}", params: { 'garden' => { 'name' => 'can i modify this' } }, headers: headers + end.to raise_error ActionController::RoutingError end - scenario '#delete' do - delete "/api/v1/gardens/#{garden.id}", {}, headers - expect(response.code).to eq "404" + it '#delete' do + expect do + delete "/api/v1/gardens/#{garden.id}", params: {}, headers: headers + end.to raise_error ActionController::RoutingError end end diff --git a/spec/requests/api/v1/harvest_request_spec.rb b/spec/requests/api/v1/harvest_request_spec.rb index 7d97dea08..ebb0b59ba 100644 --- a/spec/requests/api/v1/harvest_request_spec.rb +++ b/spec/requests/api/v1/harvest_request_spec.rb @@ -57,12 +57,12 @@ RSpec.describe 'Harvests', type: :request do subject { JSON.parse response.body } describe '#index' do - before { get '/api/v1/harvests', {}, headers } + before { get '/api/v1/harvests', params: {}, headers: headers } it { expect(subject['data']).to include(harvest_encoded_as_json_api) } end describe '#show' do - before { get "/api/v1/harvests/#{harvest.id}", {}, headers } + before { get "/api/v1/harvests/#{harvest.id}", params: {}, headers: headers } it { expect(subject['data']['attributes']).to eq(attributes) } it { expect(subject['data']['relationships']).to include("planting" => planting_as_json_api) } it { expect(subject['data']['relationships']).to include("crop" => crop_as_json_api) } @@ -71,18 +71,25 @@ RSpec.describe 'Harvests', type: :request do it { expect(subject['data']).to eq(harvest_encoded_as_json_api) } end - describe '#create' do - before { post '/api/v1/harvests', { 'harvest' => { 'description' => 'can i make this' } }, headers } - it { expect(response.code).to eq "404" } + it '#create' do + expect do + put '/api/v1/harvests', headers: headers, params: { + 'harvest' => { 'description' => 'can i make this' } + } + end.to raise_error ActionController::RoutingError end - describe '#update' do - before { post "/api/v1/harvests/#{harvest.id}", { 'harvest' => { 'description' => 'can i modify this' } }, headers } - it { expect(response.code).to eq "404" } + it '#update' do + expect do + post "/api/v1/harvests/#{harvest.id}", headers: headers, params: { + 'harvest' => { 'description' => 'can i modify this' } + } + end.to raise_error ActionController::RoutingError end - describe '#delete' do - before { delete "/api/v1/harvests/#{harvest.id}", {}, headers } - it { expect(response.code).to eq "404" } + it '#delete' do + expect do + delete "/api/v1/harvests/#{harvest.id}", headers: headers, params: {} + end.to raise_error ActionController::RoutingError end end diff --git a/spec/requests/api/v1/member_request_spec.rb b/spec/requests/api/v1/member_request_spec.rb index 0c74dc3cd..9536ec7c1 100644 --- a/spec/requests/api/v1/member_request_spec.rb +++ b/spec/requests/api/v1/member_request_spec.rb @@ -57,12 +57,12 @@ RSpec.describe 'Members', type: :request do subject { JSON.parse response.body } describe '#index' do - before { get '/api/v1/members', {}, headers } + before { get '/api/v1/members', params: {}, headers: headers } it { expect(subject['data']).to include(member_encoded_as_json_api) } end describe '#show' do - before { get "/api/v1/members/#{member.id}", {}, headers } + before { get "/api/v1/members/#{member.id}", params: {}, headers: headers } it { expect(subject['data']['relationships']).to include("gardens" => gardens_as_json_api) } it { expect(subject['data']['relationships']).to include("plantings" => plantings_as_json_api) } it { expect(subject['data']['relationships']).to include("seeds" => seeds_as_json_api) } @@ -71,18 +71,24 @@ RSpec.describe 'Members', type: :request do it { expect(subject['data']).to eq(member_encoded_as_json_api) } end - describe '#create' do - before { post '/api/v1/members', { 'member' => { 'login_name' => 'can i make this' } }, headers } - it { expect(response.code).to eq "404" } + it '#create' do + expect do + post '/api/v1/members', params: { 'member' => { 'login_name' => 'can i make this' } }, headers: headers + end.to raise_error ActionController::RoutingError end - describe '#update' do - before { post "/api/v1/members/#{member.id}", { 'member' => { 'login_name' => 'can i modify this' } }, headers } - it { expect(response.code).to eq "404" } + it '#update' do + expect do + post "/api/v1/members/#{member.id}", params: { + 'member' => { 'login_name' => 'can i modify this' } + }, + headers: headers + end.to raise_error ActionController::RoutingError end - describe '#delete' do - before { delete "/api/v1/members/#{member.id}", {}, headers } - it { expect(response.code).to eq "404" } + it '#delete' do + expect do + delete "/api/v1/members/#{member.id}", params: {}, headers: headers + end.to raise_error ActionController::RoutingError end end diff --git a/spec/requests/api/v1/photos_request_spec.rb b/spec/requests/api/v1/photos_request_spec.rb index cb205d972..3af370a54 100644 --- a/spec/requests/api/v1/photos_request_spec.rb +++ b/spec/requests/api/v1/photos_request_spec.rb @@ -55,12 +55,12 @@ RSpec.describe 'Photos', type: :request do subject { JSON.parse response.body } describe '#index' do - before { get '/api/v1/photos', {}, headers } + before { get '/api/v1/photos', params: {}, headers: headers } it { expect(subject['data']).to include(photo_encoded_as_json_api) } end describe '#show' do - before { get "/api/v1/photos/#{photo.id}", {}, headers } + before { get "/api/v1/photos/#{photo.id}", params: {}, headers: headers } it { expect(subject['data']['attributes']).to eq(attributes) } it { expect(subject['data']['relationships']).to include("plantings" => plantings_as_json_api) } it { expect(subject['data']['relationships']).to include("harvests" => harvests_as_json_api) } @@ -68,18 +68,21 @@ RSpec.describe 'Photos', type: :request do it { expect(subject['data']).to eq(photo_encoded_as_json_api) } end - describe '#create' do - before { post '/api/v1/photos', { 'photo' => { 'name' => 'can i make this' } }, headers } - it { expect(response.code).to eq "404" } + it '#create' do + expect do + post '/api/v1/photos', params: { 'photo' => { 'name' => 'can i make this' } }, headers: headers + end.to raise_error ActionController::RoutingError end - describe '#update' do - before { post "/api/v1/photos/#{photo.id}", { 'photo' => { 'name' => 'can i modify this' } }, headers } - it { expect(response.code).to eq "404" } + it '#update' do + expect do + post "/api/v1/photos/#{photo.id}", params: { 'photo' => { 'name' => 'can i modify this' } }, headers: headers + end.to raise_error ActionController::RoutingError end - describe '#delete' do - before { delete "/api/v1/photos/#{photo.id}", {}, headers } - it { expect(response.code).to eq "404" } + it '#delete' do + expect do + delete "/api/v1/photos/#{photo.id}", params: {}, headers: headers + end.to raise_error ActionController::RoutingError end end diff --git a/spec/requests/api/v1/plantings_request_spec.rb b/spec/requests/api/v1/plantings_request_spec.rb index 0591a28dc..ae561a66d 100644 --- a/spec/requests/api/v1/plantings_request_spec.rb +++ b/spec/requests/api/v1/plantings_request_spec.rb @@ -68,12 +68,12 @@ RSpec.describe 'Plantings', type: :request do subject { JSON.parse response.body } scenario '#index' do - get '/api/v1/plantings', {}, headers + get '/api/v1/plantings', params: {}, headers: headers expect(subject['data']).to include(planting_encoded_as_json_api) end scenario '#show' do - get "/api/v1/plantings/#{planting.id}", {}, headers + get "/api/v1/plantings/#{planting.id}", params: {}, headers: headers expect(subject['data']['relationships']).to include("garden" => garden_as_json_api) expect(subject['data']['relationships']).to include("crop" => crop_as_json_api) expect(subject['data']['relationships']).to include("owner" => owner_as_json_api) @@ -82,18 +82,23 @@ RSpec.describe 'Plantings', type: :request do expect(subject['data']).to eq(planting_encoded_as_json_api) end - scenario '#create' do - post '/api/v1/plantings', { 'planting' => { 'description' => 'can i make this' } }, headers - expect(response.code).to eq "404" + it '#create' do + expect do + post '/api/v1/plantings', params: { 'planting' => { 'description' => 'can i make this' } }, headers: headers + end.to raise_error ActionController::RoutingError end - scenario '#update' do - post "/api/v1/plantings/#{planting.id}", { 'planting' => { 'description' => 'can i modify this' } }, headers - expect(response.code).to eq "404" + it '#update' do + expect do + post "/api/v1/plantings/#{planting.id}", headers: headers, params: { + 'planting' => { 'description' => 'can i modify this' } + } + end.to raise_error ActionController::RoutingError end - scenario '#delete' do - delete "/api/v1/plantings/#{planting.id}", {}, headers - expect(response.code).to eq "404" + it '#delete' do + expect do + delete "/api/v1/plantings/#{planting.id}", params: {}, headers: headers + end.to raise_error ActionController::RoutingError end end diff --git a/spec/requests/api/v1/seeds_request_spec.rb b/spec/requests/api/v1/seeds_request_spec.rb index 41d622a1f..243e645f5 100644 --- a/spec/requests/api/v1/seeds_request_spec.rb +++ b/spec/requests/api/v1/seeds_request_spec.rb @@ -44,30 +44,33 @@ RSpec.describe 'Photos', type: :request do subject { JSON.parse response.body } describe '#index' do - before { get '/api/v1/seeds', {}, headers } + before { get '/api/v1/seeds', params: {}, headers: headers } it { expect(subject['data']).to include(seed_encoded_as_json_api) } end describe '#show' do - before { get "/api/v1/seeds/#{seed.id}", {}, headers } + before { get "/api/v1/seeds/#{seed.id}", params: {}, headers: headers } it { expect(subject['data']['attributes']).to eq(attributes) } it { expect(subject['data']['relationships']).to include("owner" => owner_as_json_api) } it { expect(subject['data']['relationships']).to include("crop" => crop_as_json_api) } it { expect(subject['data']).to eq(seed_encoded_as_json_api) } end - describe '#create' do - before { post '/api/v1/seeds', { 'seed' => { 'name' => 'can i make this' } }, headers } - it { expect(response.code).to eq "404" } + it '#create' do + expect do + post '/api/v1/seeds', params: { 'seed' => { 'name' => 'can i make this' } }, headers: headers + end.to raise_error ActionController::RoutingError end - describe '#update' do - before { post "/api/v1/seeds/#{seed.id}", { 'seed' => { 'name' => 'can i modify this' } }, headers } - it { expect(response.code).to eq "404" } + it '#update' do + expect do + post "/api/v1/seeds/#{seed.id}", params: { 'seed' => { 'name' => 'can i modify this' } }, headers: headers + end.to raise_error ActionController::RoutingError end - describe '#delete' do - before { delete "/api/v1/seeds/#{seed.id}", {}, headers } - it { expect(response.code).to eq "404" } + it '#delete' do + expect do + delete "/api/v1/seeds/#{seed.id}", params: {}, headers: headers + end.to raise_error ActionController::RoutingError end end From 987400526e828d0a6971fff410c377f12ffed4ce Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 12:32:04 +1300 Subject: [PATCH 054/219] Use distinct for plant parts --- app/models/crop.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/crop.rb b/app/models/crop.rb index f87fbb262..4863b6658 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -15,7 +15,7 @@ class Crop < ApplicationRecord has_many :photos, through: :plantings has_many :seeds has_many :harvests - has_many :plant_parts, -> { uniq.reorder("plant_parts.name") }, through: :harvests + has_many :plant_parts, -> { distinct.reorder("plant_parts.name") }, through: :harvests belongs_to :creator, class_name: 'Member' belongs_to :requester, class_name: 'Member', optional: true belongs_to :parent, class_name: 'Crop', optional: true From 4800f405eaf023013e2d784e0b8a20b35e22d942 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 12:35:05 +1300 Subject: [PATCH 055/219] Two more fixes to tests for slug generation --- spec/models/post_spec.rb | 2 +- spec/models/seed_spec.rb | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index cb8cbcc91..08502fec7 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' describe Post do - let(:member) { FactoryBot.create(:member) } + let(:member) { FactoryBot.create(:member, login_name: 'whinacooper') } it_behaves_like "it is likeable" it "should have a slug" do diff --git a/spec/models/seed_spec.rb b/spec/models/seed_spec.rb index c1cd95cbd..d1ad584d1 100644 --- a/spec/models/seed_spec.rb +++ b/spec/models/seed_spec.rb @@ -1,7 +1,8 @@ require 'rails_helper' describe Seed do - let(:seed) { FactoryBot.build(:seed) } + let(:owner) { FactoryBot.create :owner, login_name: 'tamateapokaiwhenua' } + let(:seed) { FactoryBot.build(:seed, owner: owner) } it 'should save a basic seed' do seed.save.should be(true) @@ -9,7 +10,7 @@ describe Seed do it "should have a slug" do seed.save - seed.slug.should match(/member\d+-magic-bean/) + seed.slug.should match(/tamateapokaiwhenua-magic-bean/) end context 'quantity' do From a9043b8f5ac5fe2d231f8070305999cdc8679938 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 15:10:25 +1300 Subject: [PATCH 056/219] specify login name and post body in spec don't rely on factory to produce predictable results, specify what we're gonna be looking for in spec --- spec/views/posts/show.html.haml_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/views/posts/show.html.haml_spec.rb b/spec/views/posts/show.html.haml_spec.rb index 169e90da0..9ac4434c6 100644 --- a/spec/views/posts/show.html.haml_spec.rb +++ b/spec/views/posts/show.html.haml_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' describe "posts/show" do - let(:author) { FactoryBot.create(:member) } + let(:author) { FactoryBot.create(:member, login_name: 'mary') } before(:each) do controller.stub(:current_user) { nil } @@ -12,12 +12,12 @@ describe "posts/show" do describe 'render post' do before { render } describe "basic post" do - let(:post) { FactoryBot.create(:post, author: author) } + let(:post) { FactoryBot.create(:post, author: author, body: 'hello there') } # show the name of the member who posted the post - it { is_expected.to match(/member\d+/) } + it { is_expected.to have_text author.login_name } # Subject goes in title - it { is_expected.to have_text('This is some text.') } + it { is_expected.to have_text('hello there') } # shouldn't show the subject on a single post page # (it appears in the title/h1 via the layout, not via this view) it { is_expected.not_to have_text('An Update') } From f02d328d1c5de1d8a58eecb934c3821115903563 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 15:34:51 +1300 Subject: [PATCH 057/219] commented out a cms link --- app/views/admin/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/index.html.haml b/app/views/admin/index.html.haml index 294e16199..389cc2dee 100644 --- a/app/views/admin/index.html.haml +++ b/app/views/admin/index.html.haml @@ -10,7 +10,7 @@ %li= link_to "Products", products_path %li= link_to "Roles", roles_path %li= link_to "Forums", forums_path - %li= link_to "CMS", comfy_admin_cms_path + -# %li= link_to "CMS", comfy_admin_cms_path .col-md-4 %h2 Crop data admin From ca40d0714895c55ac6ca19a343c27e9415bf12ac Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 16:42:18 +1300 Subject: [PATCH 058/219] Fixed up crops view spec --- spec/views/home/_crops.html.haml_spec.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/spec/views/home/_crops.html.haml_spec.rb b/spec/views/home/_crops.html.haml_spec.rb index 254fcb2f6..bfc8dc856 100644 --- a/spec/views/home/_crops.html.haml_spec.rb +++ b/spec/views/home/_crops.html.haml_spec.rb @@ -1,10 +1,16 @@ require 'rails_helper' describe 'home/_crops.html.haml', type: "view" do - let!(:crop) { FactoryBot.create(:crop, plantings: FactoryBot.create_list(:planting, 3)) } - let!(:photo) { FactoryBot.create(:photo, plantings: [crop.plantings.first]) } + let(:crop) { FactoryBot.create(:crop, name: 'pūhā') } + let(:photo) { FactoryBot.create(:photo) } + before(:each) do + plantings = FactoryBot.create_list(:planting, 3, crop: crop) + plantings.each do |p| + p.photos << photo + end + render + end let(:planting) { crop.plantings.first } - before(:each) { render } it 'shows crops section' do assert_select 'h2', text: 'Some of our crops' assert_select "a[href='#{crop_path(crop)}']" From 482d15e36dd814cc0b787d2051be1010e2a41b67 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 6 Jan 2018 16:48:28 +1300 Subject: [PATCH 059/219] Fixed spec to specify login name before checking --- spec/views/members/show.rss.haml_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/views/members/show.rss.haml_spec.rb b/spec/views/members/show.rss.haml_spec.rb index 23366a9a8..0ea36d1c0 100644 --- a/spec/views/members/show.rss.haml_spec.rb +++ b/spec/views/members/show.rss.haml_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' describe 'members/show.rss.haml', type: "view" do before(:each) do - @member = assign(:member, FactoryBot.create(:member)) + @member = assign(:member, FactoryBot.create(:member, login_name: 'callum')) @post1 = FactoryBot.create(:post, id: 1, author: @member) @post2 = FactoryBot.create(:markdown_post, id: 2, author: @member) assign(:posts, [@post1, @post2]) @@ -12,7 +12,7 @@ describe 'members/show.rss.haml', type: "view" do subject { rendered } it 'shows RSS feed title' do - is_expected.to match(/member\d+'s recent posts/) + is_expected.to have_text("callum's recent posts") end it 'shows content of posts' do From bf34feacbafc98242b31decaaa5f40c4eaa018ce Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 7 Jan 2018 17:25:16 +1300 Subject: [PATCH 060/219] Convert to use chartkick gem --- .overcommit.yml | 1 + Gemfile | 3 +- Gemfile.lock | 5 +- app/assets/javascripts/charts.js | 2 + app/assets/javascripts/crops.js.erb | 37 -- app/assets/javascripts/graphs/bar_group.js | 53 --- .../javascripts/graphs/bar_label_group.js | 45 -- app/assets/javascripts/graphs/height_scale.js | 31 -- .../graphs/horizontal_bar_graph.js | 54 --- app/assets/javascripts/graphs/width_scale.js | 37 -- app/assets/javascripts/highcharts.js | 389 ++++++++++++++++++ app/assets/stylesheets/application.sass | 1 + .../custom_bootstrap/variables.sass | 1 + app/assets/stylesheets/overrides.sass | 19 - app/assets/stylesheets/predictions.sass | 11 + app/controllers/crops_controller.rb | 14 +- app/views/crops/_predictions.html.haml | 55 +-- app/views/crops/show.html.haml | 35 +- app/views/layouts/application.html.haml | 1 + config/initializers/assets.rb | 1 + config/routes.rb | 1 + 21 files changed, 470 insertions(+), 326 deletions(-) create mode 100644 app/assets/javascripts/charts.js delete mode 100644 app/assets/javascripts/graphs/bar_group.js delete mode 100644 app/assets/javascripts/graphs/bar_label_group.js delete mode 100644 app/assets/javascripts/graphs/height_scale.js delete mode 100644 app/assets/javascripts/graphs/horizontal_bar_graph.js delete mode 100644 app/assets/javascripts/graphs/width_scale.js create mode 100644 app/assets/javascripts/highcharts.js create mode 100644 app/assets/stylesheets/predictions.sass diff --git a/.overcommit.yml b/.overcommit.yml index d7899885f..200a90e42 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -61,6 +61,7 @@ PreCommit: enabled: true exclude: - 'app/assets/**' + - 'app/assets/javascripts/highcharts.js' - 'spec/javascripts/support/vendor/**' - '**/bootstrap*' command: ['./node_modules/.bin/eslint'] diff --git a/Gemfile b/Gemfile index 32158db8f..85b77b245 100644 --- a/Gemfile +++ b/Gemfile @@ -78,8 +78,7 @@ gem 'omniauth-facebook' gem 'omniauth-flickr', '>= 0.0.15' gem 'omniauth-twitter' -# For charting data -gem 'd3-rails' +gem "chartkick" # client for Elasticsearch. Elasticsearch is a flexible # and powerful, distributed, real-time search and analytics engine. diff --git a/Gemfile.lock b/Gemfile.lock index 28e049445..b7f899e78 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -91,6 +91,7 @@ GEM capybara-screenshot (1.0.18) capybara (>= 1.0, < 3) launchy + chartkick (2.2.5) childprocess (0.8.0) ffi (~> 1.0, >= 1.0.11) cliver (0.3.2) @@ -115,8 +116,6 @@ GEM crass (1.0.3) csv_shaper (1.3.0) activesupport (>= 3.0.0) - d3-rails (4.10.2) - railties (>= 3.1) dalli (2.7.6) database_cleaner (1.6.2) devise (4.4.0) @@ -499,11 +498,11 @@ DEPENDENCIES capybara capybara-email capybara-screenshot + chartkick codeclimate-test-reporter coffee-rails coveralls csv_shaper - d3-rails dalli database_cleaner devise diff --git a/app/assets/javascripts/charts.js b/app/assets/javascripts/charts.js new file mode 100644 index 000000000..1c42da456 --- /dev/null +++ b/app/assets/javascripts/charts.js @@ -0,0 +1,2 @@ +// = require Chart.bundle +// = require chartkick diff --git a/app/assets/javascripts/crops.js.erb b/app/assets/javascripts/crops.js.erb index f2eec8848..ad438190a 100644 --- a/app/assets/javascripts/crops.js.erb +++ b/app/assets/javascripts/crops.js.erb @@ -1,5 +1,3 @@ -//= require graphs/horizontal_bar_graph - if (document.getElementById("cropmap") !== null) { mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Rails.application.config.mapbox_map_id %>"; mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Rails.application.config.mapbox_access_token %>"; @@ -51,41 +49,6 @@ function showCropMap(cropmap) { cropmap.addLayer(markers); } -function plantingStats(crop) { - var sunniness_counts = { 'empty': 0, 'sun': 0, 'semi-shade': 0, 'shade': 0 }; - $.each(crop.plantings, function(i, planting) { - if (planting.sunniness) { - sunniness_counts[planting.sunniness]++; - } else { - sunniness_counts['Empty']++; - } - }); - return [ - {name: 'Empty', value: sunniness_counts['empty']}, - {name: 'Sun', value: sunniness_counts['sun']}, - {name: 'Semi-shade', value: sunniness_counts['semi-shade']}, - {name: 'Shade', value: sunniness_counts['shade']} - ]; -} - -if ($("#sunchart")[0] !== null) { - var HorizontalBarGraph = growstuff.HorizontalBarGraph; - $.getJSON(location.pathname + '.json', function (crop) { - data = { - bars: plantingStats(crop), - bar_color: 'steelblue', - width: {size: 300, scale: 'linear'}, - height: {size: 100, scale: 'ordinal'}, - //left is used to shift the bars over so that there is - //room for the labels - margin: {top: 0, right: 0, bottom: 0, left: 100} - }; - - var graph = new HorizontalBarGraph(data); - graph.render(d3.select($('#sunchart')[0])); - }); -} - $('.btn.toggle.crop-hierarchy').click(function () { $('.toggle.crop-hierarchy').toggleClass('hide'); }); diff --git a/app/assets/javascripts/graphs/bar_group.js b/app/assets/javascripts/graphs/bar_group.js deleted file mode 100644 index e8a90c20a..000000000 --- a/app/assets/javascripts/graphs/bar_group.js +++ /dev/null @@ -1,53 +0,0 @@ -// =require d3 -// = require graphs/width_scale -// = require graphs/height_scale - -/* - * This represents bars for a bar graph. - * Currently these are used for HorizontalBarGraph. - */ -(function() { - 'use strict'; - - - var growstuff = (window.growstuff = window.growstuff || {}); - var WidthScale = growstuff.WidthScale; - var HeightScale = growstuff.HeightScale; - -/** - * data object for bar group - * @param {Object} data Graph configuration - */ -function BarGroup(data) { - this._data = data; -} - BarGroup.prototype.render = function(root) { - var data = this._data; - var bars = this._data.bars; - var widthScale = new WidthScale(data).render(); - var heightScale = new HeightScale(data).render(); - - return root.append('g') - .attr('class', 'bar') - .selectAll('rect') - .data(bars.map(function(bar) { - return bar.value; - })) - .enter() - .append('rect') - .attr('y', function(d, i) { - return heightScale(i); - }) - .attr('height', heightScale.rangeBand()) - .attr('fill', data.bar_color) - .attr('width', function(d) { - return widthScale(d); - }) - .append('title') - .text(function(d) { - return 'This value is ' + d + '.'; - }); - }; - - growstuff.BarGroup = BarGroup; -}()); diff --git a/app/assets/javascripts/graphs/bar_label_group.js b/app/assets/javascripts/graphs/bar_label_group.js deleted file mode 100644 index 2da31f567..000000000 --- a/app/assets/javascripts/graphs/bar_label_group.js +++ /dev/null @@ -1,45 +0,0 @@ -// =require d3 -/** - * This file draws the labels to the left of each bar. - */ -(function() { - 'use strict'; - - var growstuff = (window.growstuff = window.growstuff || {}); - /** - * new bar label object - * @param {Object} data Graph configuration - */ - function BarLabelGroup(data) { - this._data = data; - } - - BarLabelGroup.prototype.render = function(d3) { - var bars = this._data.bars; - // vvcopy pasta from spike vv -- this is a good candidate for refactor - var barHeight = 40; - - return d3.append('g') - .attr('class', 'bar-label') - .selectAll('text') - .data(bars.map(function(bar) { - return bar.name; - })) - .enter() - .append('text') - .attr('x', -80) - .attr('y', function(d, i) { - // shrink the margin between each label to give them an even spread with - // bars - var barLabelSpread = 2/3; - // move them downward to line up with bars - var barLabelTopEdge = 17; - return i * barHeight * (barLabelSpread) + barLabelTopEdge; - }) - .text(function(d) { - return d; - }); - }; - - growstuff.BarLabelGroup = BarLabelGroup; -}()); diff --git a/app/assets/javascripts/graphs/height_scale.js b/app/assets/javascripts/graphs/height_scale.js deleted file mode 100644 index 5189ef800..000000000 --- a/app/assets/javascripts/graphs/height_scale.js +++ /dev/null @@ -1,31 +0,0 @@ -// =require d3 - -/** - * Height Scale is used to map the number of bars to the display size of - * the svg - */ - -(function() { - 'use strict'; - - var growstuff = (window.growstuff = window.growstuff || {}); - - /** - * new height scale object - * @param {Object} data Graph configuration - */ - function HeightScale(data) { - this._data = data; - } - - HeightScale.prototype.render = function() { - var data = this._data; - var scaleType = data.height.scale; - - return d3.scale[scaleType]() - .domain(d3.range(data.bars.length)) - .rangeRoundBands([0, data.height.size], 0.05, 0); - }; - - growstuff.HeightScale = HeightScale; -}()); diff --git a/app/assets/javascripts/graphs/horizontal_bar_graph.js b/app/assets/javascripts/graphs/horizontal_bar_graph.js deleted file mode 100644 index ef2333ef1..000000000 --- a/app/assets/javascripts/graphs/horizontal_bar_graph.js +++ /dev/null @@ -1,54 +0,0 @@ -// = require d3 -// = require graphs/bar_group -// = require graphs/bar_label_group - -/** - * Horizontal Bar Graph represents sum total of the graph including all of the parts: - * Bars - * Bar Labels - * - * The main dimensions of the graph are rendered here. - */ - -(function() { - 'use strict'; - - var growstuff = (window.growstuff = window.growstuff || {}); - var BarGroup = growstuff.BarGroup; - var BarLabelGroup = growstuff.BarLabelGroup; - - /** - * create a new graph object - * @param {Object} data Graph configuration - */ - function HorizontalBarGraph(data) { - this._data = data; - this._d3 = d3; - } - - HorizontalBarGraph.prototype.render = function(root) { - var width = this._data.width; - var height = this._data.height; - - var barLabelGroup = new BarLabelGroup(this._data); - var margin = this._data.margin; - - var barGroup = new BarGroup(this._data); - - var svg = root - .append('svg') - .attr('width', width.size + margin.left + margin.right) - .attr('height', height.size + margin.top + margin.bottom) - .append('g') - .attr('class', 'bar-graph') - .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - - - barGroup.render(svg); - barLabelGroup.render(svg); - - return svg; - }; - - growstuff.HorizontalBarGraph = HorizontalBarGraph; -}()); diff --git a/app/assets/javascripts/graphs/width_scale.js b/app/assets/javascripts/graphs/width_scale.js deleted file mode 100644 index 7f2b9dda2..000000000 --- a/app/assets/javascripts/graphs/width_scale.js +++ /dev/null @@ -1,37 +0,0 @@ -// =require d3 - -/** - * Width scale is used to map the value for the length of each bar - * to the display size of the svg - */ -(function() { - 'use strict'; - - var growstuff = (window.growstuff = window.growstuff || {}); - - /** - * Object for WidthScale - * @param {Object} data Graph configuration - */ - function WidthScale(data) { - this._data = data; - } - - WidthScale.prototype.render = function() { - var data = this._data; - var scaleType = data.width.scale; - var axisSize = data.width.size; - - return d3.scale[scaleType]() - .domain([0, this.getMaxValue()]) - .range([0, axisSize]); - }; - - WidthScale.prototype.getMaxValue = function() { - return d3.max(this._data.bars.map(function(bar) { - return bar.value; - })); - }; - - growstuff.WidthScale = WidthScale; -}()); diff --git a/app/assets/javascripts/highcharts.js b/app/assets/javascripts/highcharts.js new file mode 100644 index 000000000..9554ef58b --- /dev/null +++ b/app/assets/javascripts/highcharts.js @@ -0,0 +1,389 @@ +/* + Highcharts JS v6.0.4 (2017-12-15) + + (c) 2009-2016 Torstein Honsi + + License: www.highcharts.com/license +*/ +(function(S,M){"object"===typeof module&&module.exports?module.exports=S.document?M(S):M:S.Highcharts=M(S)})("undefined"!==typeof window?window:this,function(S){var M=function(){var a="undefined"===typeof S?window:S,E=a.document,D=a.navigator&&a.navigator.userAgent||"",H=E&&E.createElementNS&&!!E.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect,p=/(edge|msie|trident)/i.test(D)&&!a.opera,f=/Firefox/.test(D),l=f&&4>parseInt(D.split("Firefox/")[1],10);return a.Highcharts?a.Highcharts.error(16, +!0):{product:"Highcharts",version:"6.0.4",deg2rad:2*Math.PI/360,doc:E,hasBidiBug:l,hasTouch:E&&void 0!==E.documentElement.ontouchstart,isMS:p,isWebKit:/AppleWebKit/.test(D),isFirefox:f,isTouchDevice:/(Mobile|Android|Windows Phone)/.test(D),SVG_NS:"http://www.w3.org/2000/svg",chartCount:0,seriesTypes:{},symbolSizes:{},svg:H,win:a,marginNames:["plotTop","marginRight","marginBottom","plotLeft"],noop:function(){},charts:[]}}();(function(a){a.timers=[];var E=a.charts,D=a.doc,H=a.win;a.error=function(p, +f){p=a.isNumber(p)?"Highcharts error #"+p+": www.highcharts.com/errors/"+p:p;if(f)throw Error(p);H.console&&console.log(p)};a.Fx=function(a,f,l){this.options=f;this.elem=a;this.prop=l};a.Fx.prototype={dSetter:function(){var a=this.paths[0],f=this.paths[1],l=[],r=this.now,n=a.length,w;if(1===r)l=this.toD;else if(n===f.length&&1>r)for(;n--;)w=parseFloat(a[n]),l[n]=isNaN(w)?f[n]:r*parseFloat(f[n]-w)+w;else l=f;this.elem.attr("d",l,null,!0)},update:function(){var a=this.elem,f=this.prop,l=this.now,r= +this.options.step;if(this[f+"Setter"])this[f+"Setter"]();else a.attr?a.element&&a.attr(f,l,null,!0):a.style[f]=l+this.unit;r&&r.call(a,l,this)},run:function(p,f,l){var r=this,n=r.options,w=function(a){return w.stopped?!1:r.step(a)},u=H.requestAnimationFrame||function(a){setTimeout(a,13)},e=function(){for(var h=0;h=u+this.startTime?(this.now=this.end,this.pos=1,this.update(),l=e[this.prop]=!0,a.objectEach(e,function(a){!0!==a&&(l=!1)}),l&&w&&w.call(n),p=!1):(this.pos=r.easing((f-this.startTime)/u),this.now=this.start+(this.end- +this.start)*this.pos,this.update(),p=!0);return p},initPath:function(p,f,l){function r(a){var b,c;for(k=a.length;k--;)b="M"===a[k]||"L"===a[k],c=/[a-zA-Z]/.test(a[k+3]),b&&c&&a.splice(k+1,0,a[k+1],a[k+2],a[k+1],a[k+2])}function n(a,b){for(;a.lengtha&&-Infinityw?"AM":"PM",P:12>w?"am":"pm",S:k(n.getSeconds()),L:k(Math.round(f%1E3),3)},a.dateFormats);a.objectEach(r,function(a,b){for(;-1!==p.indexOf("%"+b);)p=p.replace("%"+b,"function"===typeof a?a(f):a)});return l?p.substr(0,1).toUpperCase()+p.substr(1):p};a.formatSingle=function(p,f){var l=/\.([0-9])/,r=a.defaultOptions.lang;/f$/.test(p)?(l=(l=p.match(l))?l[1]:-1,null!==f&&(f=a.numberFormat(f,l,r.decimalPoint,-1=l&&(f=[1/l])));for(r=0;r=p||!n&&w<=(f[r]+(f[r+1]||f[r]))/2);r++);return u=a.correctFloat(u*l,-Math.round(Math.log(.001)/Math.LN10))};a.stableSort=function(a,f){var l=a.length,p,n;for(n=0;nl&&(l=a[f]);return l};a.destroyObjectProperties=function(p,f){a.objectEach(p,function(a,r){a&&a!==f&&a.destroy&&a.destroy();delete p[r]})};a.discardElement=function(p){var f=a.garbageBin;f||(f=a.createElement("div"));p&&f.appendChild(p);f.innerHTML=""};a.correctFloat=function(a,f){return parseFloat(a.toPrecision(f||14))};a.setAnimation=function(p,f){f.renderer.globalAnimation=a.pick(p,f.options.chart.animation, +!0)};a.animObject=function(p){return a.isObject(p)?a.merge(p):{duration:p?500:0}};a.timeUnits={millisecond:1,second:1E3,minute:6E4,hour:36E5,day:864E5,week:6048E5,month:24192E5,year:314496E5};a.numberFormat=function(p,f,l,r){p=+p||0;f=+f;var n=a.defaultOptions.lang,w=(p.toString().split(".")[1]||"").split("e")[0].length,u,e,h=p.toString().split("e");-1===f?f=Math.min(w,20):a.isNumber(f)?f&&h[1]&&0>h[1]&&(u=f+ +h[1],0<=u?(h[0]=(+h[0]).toExponential(u).split("e")[0],f=u):(h[0]=h[0].split(".")[0]||0, +p=20>f?(h[0]*Math.pow(10,h[1])).toFixed(f):0,h[1]=0)):f=2;e=(Math.abs(h[1]?h[0]:p)+Math.pow(10,-Math.max(f,w)-1)).toFixed(f);w=String(a.pInt(e));u=3p?"-":"")+(u?w.substr(0,u)+r:"");p+=w.substr(u).replace(/(\d{3})(?=\d)/g,"$1"+r);f&&(p+=l+e.slice(-f));h[1]&&0!==+p&&(p+="e"+h[1]);return p};Math.easeInOutSine=function(a){return-.5*(Math.cos(Math.PI*a)-1)};a.getStyle=function(p,f,l){if("width"===f)return Math.min(p.offsetWidth, +p.scrollWidth)-a.getStyle(p,"padding-left")-a.getStyle(p,"padding-right");if("height"===f)return Math.min(p.offsetHeight,p.scrollHeight)-a.getStyle(p,"padding-top")-a.getStyle(p,"padding-bottom");H.getComputedStyle||a.error(27,!0);if(p=H.getComputedStyle(p,void 0))p=p.getPropertyValue(f),a.pick(l,"opacity"!==f)&&(p=a.pInt(p));return p};a.inArray=function(p,f){return(a.indexOfPolyfill||Array.prototype.indexOf).call(f,p)};a.grep=function(p,f){return(a.filterPolyfill||Array.prototype.filter).call(p, +f)};a.find=Array.prototype.find?function(a,f){return a.find(f)}:function(a,f){var l,r=a.length;for(l=0;l>16,(l&65280)>> +8,l&255,1]:4===f&&(n=[(l&3840)>>4|(l&3840)>>8,(l&240)>>4|l&240,(l&15)<<4|l&15,1])),!n)for(w=this.parsers.length;w--&&!n;)u=this.parsers[w],(f=u.regex.exec(l))&&(n=u.parse(f));this.rgba=n||[]},get:function(a){var f=this.input,n=this.rgba,l;this.stops?(l=p(f),l.stops=[].concat(l.stops),E(this.stops,function(n,e){l.stops[e]=[l.stops[e][0],n.get(a)]})):l=n&&D(n[0])?"rgb"===a||!a&&1===n[3]?"rgb("+n[0]+","+n[1]+","+n[2]+")":"a"===a?n[3]:"rgba("+n.join(",")+")":f;return l},brighten:function(a){var l,n=this.rgba; +if(this.stops)E(this.stops,function(n){n.brighten(a)});else if(D(a)&&0!==a)for(l=0;3>l;l++)n[l]+=f(255*a),0>n[l]&&(n[l]=0),255y.width)y={width:0,height:0}}else y=this.htmlGetBBox();b.isSVG&& +(a=y.width,b=y.height,q&&"11px"===q.fontSize&&17===Math.round(b)&&(y.height=b=14),g&&(y.width=Math.abs(b*Math.sin(v))+Math.abs(a*Math.cos(v)),y.height=Math.abs(b*Math.cos(v))+Math.abs(a*Math.sin(v))));if(A&&0]*>/g,"")))},textSetter:function(a){a!==this.textStr&&(delete this.bBox,this.textStr=a,this.added&&this.renderer.buildText(this))},fillSetter:function(a,g,b){"string"===typeof a?b.setAttribute(g,a):a&&this.colorGradient(a,g,b)},visibilitySetter:function(a,g,b){"inherit"===a?b.removeAttribute(g):this[g]!==a&&b.setAttribute(g,a);this[g]=a},zIndexSetter:function(a,b){var v=this.renderer,y=this.parentGroup,c=(y||v).element||v.box,k,d=this.element,q,e,v=c===v.box;k=this.added;var z;u(a)&& +(d.zIndex=a,a=+a,this[b]===a&&(k=!1),this[b]=a);if(k){(a=this.zIndex)&&y&&(y.handleZ=!0);b=c.childNodes;for(z=b.length-1;0<=z&&!q;z--)if(y=b[z],k=y.zIndex,e=!u(k),y!==d)if(0>a&&e&&!v&&!z)c.insertBefore(d,b[z]),q=!0;else if(g(k)<=a||e&&(!u(a)||0<=a))c.insertBefore(d,b[z+1]||null),q=!0;q||(c.insertBefore(d,b[v?3:0]||null),q=!0)}return q},_defaultSetter:function(a,g,b){b.setAttribute(g,a)}});E.prototype.yGetter=E.prototype.xGetter;E.prototype.translateXSetter=E.prototype.translateYSetter=E.prototype.rotationSetter= +E.prototype.verticalAlignSetter=E.prototype.rotationOriginXSetter=E.prototype.rotationOriginYSetter=E.prototype.scaleXSetter=E.prototype.scaleYSetter=E.prototype.matrixSetter=function(a,g){this[g]=a;this.doTransform=!0};E.prototype["stroke-widthSetter"]=E.prototype.strokeSetter=function(a,g,b){this[g]=a;this.stroke&&this["stroke-width"]?(E.prototype.fillSetter.call(this,this.stroke,"stroke",b),b.setAttribute("stroke-width",this["stroke-width"]),this.hasStroke=!0):"stroke-width"===g&&0===a&&this.hasStroke&& +(b.removeAttribute("stroke"),this.hasStroke=!1)};D=a.SVGRenderer=function(){this.init.apply(this,arguments)};c(D.prototype,{Element:E,SVG_NS:P,init:function(a,g,b,v,c,k){var y;v=this.createElement("svg").attr({version:"1.1","class":"highcharts-root"}).css(this.getStyle(v));y=v.element;a.appendChild(y);f(a,"dir","ltr");-1===a.innerHTML.indexOf("xmlns")&&f(y,"xmlns",this.SVG_NS);this.isSVG=!0;this.box=y;this.boxWrapper=v;this.alignedObjects=[];this.url=(x||N)&&m.getElementsByTagName("base").length? +R.location.href.replace(/#.*?$/,"").replace(/<[^>]*>/g,"").replace(/([\('\)])/g,"\\$1").replace(/ /g,"%20"):"";this.createElement("desc").add().element.appendChild(m.createTextNode("Created with Highcharts 6.0.4"));this.defs=this.createElement("defs").add();this.allowHTML=k;this.forExport=c;this.gradients={};this.cache={};this.cacheKeys=[];this.imgCount=0;this.setSize(g,b,!1);var d;x&&a.getBoundingClientRect&&(g=function(){n(a,{left:0,top:0});d=a.getBoundingClientRect();n(a,{left:Math.ceil(d.left)- +d.left+"px",top:Math.ceil(d.top)-d.top+"px"})},g(),this.unSubPixelFix=H(R,"resize",g))},getStyle:function(a){return this.style=c({fontFamily:'"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif',fontSize:"12px"},a)},setStyle:function(a){this.boxWrapper.css(this.getStyle(a))},isHidden:function(){return!this.boxWrapper.getBBox().width},destroy:function(){var a=this.defs;this.box=null;this.boxWrapper=this.boxWrapper.destroy();h(this.gradients||{});this.gradients=null;a&&(this.defs=a.destroy()); +this.unSubPixelFix&&this.unSubPixelFix();return this.alignedObjects=null},createElement:function(a){var g=new this.Element;g.init(this,a);return g},draw:A,getRadialAttr:function(a,g){return{cx:a[0]-a[2]/2+g.cx*a[2],cy:a[1]-a[2]/2+g.cy*a[2],r:g.r*a[2]}},getSpanWidth:function(a,g){var b=a.getBBox(!0).width;!L&&this.forExport&&(b=this.measureSpanWidth(g.firstChild.data,a.styles));return b},applyEllipsis:function(a,g,b,v){var c=a.rotation,y=b,k,d=0,q=b.length,e=function(a){g.removeChild(g.firstChild); +a&&g.appendChild(m.createTextNode(a))},z;a.rotation=0;y=this.getSpanWidth(a,g);if(z=y>v){for(;d<=q;)k=Math.ceil((d+q)/2),y=b.substring(0,k)+"\u2026",e(y),y=this.getSpanWidth(a,g),d===q?d=q+1:y>v?q=k-1:d=k;0===q&&e("")}a.rotation=c;return z},escapes:{"\x26":"\x26amp;","\x3c":"\x26lt;","\x3e":"\x26gt;","'":"\x26#39;",'"':"\x26quot;"},buildText:function(a){var b=a.element,v=this,c=v.forExport,y=G(a.textStr,"").toString(),q=-1!==y.indexOf("\x3c"),e=b.childNodes,z,h,A,J,t=f(b,"x"),x=a.styles,B=a.textWidth, +l=x&&x.lineHeight,C=x&&x.textOutline,u=x&&"ellipsis"===x.textOverflow,Q=x&&"nowrap"===x.whiteSpace,w=x&&x.fontSize,R,I,r=e.length,x=B&&!a.added&&this.box,p=function(a){var c;c=/(px|em)$/.test(a&&a.style.fontSize)?a.style.fontSize:w||v.style.fontSize||12;return l?g(l):v.fontMetrics(c,a.getAttribute("style")?a:b).h},K=function(a){F(v.escapes,function(g,b){a=a.replace(new RegExp(g,"g"),b)});return a};R=[y,u,Q,l,C,w,B].join();if(R!==a.textCache){for(a.textCache=R;r--;)b.removeChild(e[r]);q||C||u||B|| +-1!==y.indexOf(" ")?(z=/<.*class="([^"]+)".*>/,h=/<.*style="([^"]+)".*>/,A=/<.*href="([^"]+)".*>/,x&&x.appendChild(b),y=q?y.replace(/<(b|strong)>/g,'\x3cspan style\x3d"font-weight:bold"\x3e').replace(/<(i|em)>/g,'\x3cspan style\x3d"font-style:italic"\x3e').replace(//g,"\x3c/span\x3e").split(//g):[y],y=k(y,function(a){return""!==a}),d(y,function(g,y){var k,q=0;g=g.replace(/^\s+|\s+$/g,"").replace(//g,"\x3c/span\x3e|||"); +k=g.split("|||");d(k,function(g){if(""!==g||1===k.length){var d={},e=m.createElementNS(v.SVG_NS,"tspan"),x,F;z.test(g)&&(x=g.match(z)[1],f(e,"class",x));h.test(g)&&(F=g.match(h)[1].replace(/(;| |^)color([ :])/,"$1fill$2"),f(e,"style",F));A.test(g)&&!c&&(f(e,"onclick",'location.href\x3d"'+g.match(A)[1]+'"'),f(e,"class","highcharts-anchor"),n(e,{cursor:"pointer"}));g=K(g.replace(/<[a-zA-Z\/](.|\n)*?>/g,"")||" ");if(" "!==g){e.appendChild(m.createTextNode(g));q?d.dx=0:y&&null!==t&&(d.x=t);f(e,d);b.appendChild(e); +!q&&I&&(!L&&c&&n(e,{display:"block"}),f(e,"dy",p(e)));if(B){d=g.replace(/([^\^])-/g,"$1- ").split(" ");x=1B,void 0===J&&(J=g),g&&1!==d.length?(e.removeChild(e.firstChild),O.unshift(d.pop())):(d=O,O=[],d.length&&!Q&&(e=m.createElementNS(P,"tspan"),f(e,{dy:C,x:t}),F&&f(e,"style",F),b.appendChild(e)),l>B&&(B=l)),d.length&&e.appendChild(m.createTextNode(d.join(" ").replace(/- /g, +"-")));a.rotation=G}q++}}});I=I||b.childNodes.length}),J&&a.attr("title",a.textStr),x&&x.removeChild(b),C&&a.applyTextOutline&&a.applyTextOutline(C)):b.appendChild(m.createTextNode(K(y)))}},getContrast:function(a){a=r(a).rgba;return 510Math.abs(c.end-c.start-2*Math.PI));var y=Math.cos(k),z=Math.sin(k),h=Math.cos(e),e=Math.sin(e);c=.001>c.end-k-Math.PI?0:1;d=["M",a+d*y,g+q*z,"A",d,q,0,c,1,a+d*h,g+q*e];u(b)&&d.push(v?"M":"L",a+b* +h,g+b*e,"A",b,b,0,c,0,a+b*y,g+b*z);d.push(v?"":"Z");return d},callout:function(a,g,b,v,c){var d=Math.min(c&&c.r||0,b,v),k=d+6,q=c&&c.anchorX;c=c&&c.anchorY;var e;e=["M",a+d,g,"L",a+b-d,g,"C",a+b,g,a+b,g,a+b,g+d,"L",a+b,g+v-d,"C",a+b,g+v,a+b,g+v,a+b-d,g+v,"L",a+d,g+v,"C",a,g+v,a,g+v,a,g+v-d,"L",a,g+d,"C",a,g,a,g,a+d,g];q&&q>b?c>g+k&&cq?c>g+k&&cv&&q>a+k&&qc&&q>a+k&&qa?a+3:Math.round(1.2*a);return{h:b,b:Math.round(.8* +b),f:a}},rotCorr:function(a,g,b){var v=a;g&&b&&(v=Math.max(v*Math.cos(g*e),4));return{x:-a/3*Math.sin(g*e),y:v}},label:function(g,b,k,e,z,h,m,A,L){var y=this,J=y.g("button"!==L&&"label"),t=J.text=y.text("",0,0,m).attr({zIndex:1}),x,F,n=0,B=3,l=0,C,f,Q,G,w,R={},I,P,r=/^url\((.*?)\)$/.test(e),p=r,K,O,N,T;L&&J.addClass("highcharts-"+L);p=r;K=function(){return(I||0)%2/2};O=function(){var a=t.element.style,g={};F=(void 0===C||void 0===f||w)&&u(t.textStr)&&t.getBBox();J.width=(C||F.width||0)+2*B+l;J.height= +(f||F.height||0)+2*B;P=B+y.fontMetrics(a&&a.fontSize,t).b;p&&(x||(J.box=x=y.symbols[e]||r?y.symbol(e):y.rect(),x.addClass(("button"===L?"":"highcharts-label-box")+(L?" highcharts-"+L+"-box":"")),x.add(J),a=K(),g.x=a,g.y=(A?-P:0)+a),g.width=Math.round(J.width),g.height=Math.round(J.height),x.attr(c(g,R)),R={})};N=function(){var a=l+B,g;g=A?0:P;u(C)&&F&&("center"===w||"right"===w)&&(a+={center:.5,right:1}[w]*(C-F.width));if(a!==t.x||g!==t.y)t.attr("x",a),void 0!==g&&t.attr("y",g);t.x=a;t.y=g};T=function(a, +g){x?x.attr(a,g):R[a]=g};J.onAdd=function(){t.add(J);J.attr({text:g||0===g?g:"",x:b,y:k});x&&u(z)&&J.attr({anchorX:z,anchorY:h})};J.widthSetter=function(g){C=a.isNumber(g)?g:null};J.heightSetter=function(a){f=a};J["text-alignSetter"]=function(a){w=a};J.paddingSetter=function(a){u(a)&&a!==B&&(B=J.padding=a,N())};J.paddingLeftSetter=function(a){u(a)&&a!==l&&(l=a,N())};J.alignSetter=function(a){a={left:0,center:.5,right:1}[a];a!==n&&(n=a,F&&J.attr({x:Q}))};J.textSetter=function(a){void 0!==a&&t.textSetter(a); +O();N()};J["stroke-widthSetter"]=function(a,g){a&&(p=!0);I=this["stroke-width"]=a;T(g,a)};J.strokeSetter=J.fillSetter=J.rSetter=function(a,g){"r"!==g&&("fill"===g&&a&&(p=!0),J[g]=a);T(g,a)};J.anchorXSetter=function(a,g){z=J.anchorX=a;T(g,Math.round(a)-K()-Q)};J.anchorYSetter=function(a,g){h=J.anchorY=a;T(g,a-G)};J.xSetter=function(a){J.x=a;n&&(a-=n*((C||F.width)+2*B));Q=Math.round(a);J.attr("translateX",Q)};J.ySetter=function(a){G=J.y=Math.round(a);J.attr("translateY",G)};var U=J.css;return c(J,{css:function(a){if(a){var g= +{};a=q(a);d(J.textProps,function(b){void 0!==a[b]&&(g[b]=a[b],delete a[b])});t.css(g)}return U.call(J,a)},getBBox:function(){return{width:F.width+2*B,height:F.height+2*B,x:F.x-B,y:F.y-B}},shadow:function(a){a&&(O(),x&&x.shadow(a));return J},destroy:function(){v(J.element,"mouseenter");v(J.element,"mouseleave");t&&(t=t.destroy());x&&(x=x.destroy());E.prototype.destroy.call(J);J=y=O=N=T=null}})}});a.Renderer=D})(M);(function(a){var E=a.attr,D=a.createElement,H=a.css,p=a.defined,f=a.each,l=a.extend, +r=a.isFirefox,n=a.isMS,w=a.isWebKit,u=a.pick,e=a.pInt,h=a.SVGRenderer,m=a.win,d=a.wrap;l(a.SVGElement.prototype,{htmlCss:function(a){var b=this.element;if(b=a&&"SPAN"===b.tagName&&a.width)delete a.width,this.textWidth=b,this.updateTransform();a&&"ellipsis"===a.textOverflow&&(a.whiteSpace="nowrap",a.overflow="hidden");this.styles=l(this.styles,a);H(this.element,a);return this},htmlGetBBox:function(){var a=this.element;return{x:a.offsetLeft,y:a.offsetTop,width:a.offsetWidth,height:a.offsetHeight}}, +htmlUpdateTransform:function(){if(this.added){var a=this.renderer,b=this.element,d=this.translateX||0,z=this.translateY||0,h=this.x||0,m=this.y||0,x=this.textAlign||"left",n={left:0,center:.5,right:1}[x],t=this.styles;H(b,{marginLeft:d,marginTop:z});this.shadows&&f(this.shadows,function(a){H(a,{marginLeft:d+1,marginTop:z+1})});this.inverted&&f(b.childNodes,function(c){a.invertChild(c,b)});if("SPAN"===b.tagName){var l=this.rotation,u=e(this.textWidth),q=t&&t.whiteSpace,A=[l,x,b.innerHTML,this.textWidth, +this.textAlign].join();A!==this.cTT&&(t=a.fontMetrics(b.style.fontSize).b,p(l)&&this.setSpanRotation(l,n,t),H(b,{width:"",whiteSpace:q||"nowrap"}),b.offsetWidth>u&&/[ \-]/.test(b.textContent||b.innerText)&&H(b,{width:u+"px",display:"block",whiteSpace:q||"normal"}),this.getSpanCorrection(b.offsetWidth,t,n,l,x));H(b,{left:h+(this.xCorr||0)+"px",top:m+(this.yCorr||0)+"px"});w&&(t=b.offsetHeight);this.cTT=A}}else this.alignOnAdd=!0},setSpanRotation:function(a,b,d){var c={},k=this.renderer.getTransformKey(); +c[k]=c.transform="rotate("+a+"deg)";c[k+(r?"Origin":"-origin")]=c.transformOrigin=100*b+"% "+d+"px";H(this.element,c)},getSpanCorrection:function(a,b,d){this.xCorr=-a*d;this.yCorr=-b}});l(h.prototype,{getTransformKey:function(){return n&&!/Edge/.test(m.navigator.userAgent)?"-ms-transform":w?"-webkit-transform":r?"MozTransform":m.opera?"-o-transform":""},html:function(a,b,k){var c=this.createElement("span"),e=c.element,h=c.renderer,m=h.isSVG,w=function(a,b){f(["opacity","visibility"],function(c){d(a, +c+"Setter",function(a,c,d,k){a.call(this,c,d,k);b[d]=c})})};c.textSetter=function(a){a!==e.innerHTML&&delete this.bBox;this.textStr=a;e.innerHTML=u(a,"");c.htmlUpdateTransform()};m&&w(c,c.element.style);c.xSetter=c.ySetter=c.alignSetter=c.rotationSetter=function(a,b){"align"===b&&(b="textAlign");c[b]=a;c.htmlUpdateTransform()};c.attr({text:a,x:Math.round(b),y:Math.round(k)}).css({fontFamily:this.style.fontFamily,fontSize:this.style.fontSize,position:"absolute"});e.style.whiteSpace="nowrap";c.css= +c.htmlCss;m&&(c.add=function(a){var b,d=h.box.parentNode,k=[];if(this.parentGroup=a){if(b=a.div,!b){for(;a;)k.push(a),a=a.parentGroup;f(k.reverse(),function(a){function e(g,b){a[b]=g;n?q[h.getTransformKey()]="translate("+(a.x||a.translateX)+"px,"+(a.y||a.translateY)+"px)":"translateX"===b?q.left=g+"px":q.top=g+"px";a.doTransform=!0}var q,g=E(a.element,"class");g&&(g={className:g});b=a.div=a.div||D("div",g,{position:"absolute",left:(a.translateX||0)+"px",top:(a.translateY||0)+"px",display:a.display, +opacity:a.opacity,pointerEvents:a.styles&&a.styles.pointerEvents},b||d);q=b.style;l(a,{classSetter:function(a){return function(g){this.element.setAttribute("class",g);a.className=g}}(b),on:function(){k[0].div&&c.on.apply({element:k[0].div},arguments);return a},translateXSetter:e,translateYSetter:e});w(a,q)})}}else b=d;b.appendChild(e);c.added=!0;c.alignOnAdd&&c.htmlUpdateTransform();return c});return c}})})(M);(function(a){function E(){var n=a.defaultOptions.global,l=r.moment;if(n.timezone){if(l)return function(a){return-l.tz(a, +n.timezone).utcOffset()};a.error(25)}return n.useUTC&&n.getTimezoneOffset}function D(){var n=a.defaultOptions.global,f,u=n.useUTC,e=u?"getUTC":"get",h=u?"setUTC":"set",m="Minutes Hours Day Date Month FullYear".split(" "),d=m.concat(["Milliseconds","Seconds"]);a.Date=f=n.Date||r.Date;f.hcTimezoneOffset=u&&n.timezoneOffset;f.hcGetTimezoneOffset=E();f.hcHasTimeZone=!(!f.hcTimezoneOffset&&!f.hcGetTimezoneOffset);f.hcMakeTime=function(a,b,d,e,h,m){var c;u?(c=f.UTC.apply(0,arguments),c+=p(c)):c=(new f(a, +b,l(d,1),l(e,0),l(h,0),l(m,0))).getTime();return c};for(n=0;nb&&e-k*zm&&(p=Math.round((h-e)/Math.cos(b*r)));else if(h=e+(1-k)*z,e-k*zm&&(I=m-a.x+I*k,x=-1),I=Math.min(B,I),II||f.autoRotation&&(c.styles||{}).width)p=I;p&&(t.width=p,(n.style||{}).textOverflow||(t.textOverflow="ellipsis"),c.css(t))},getPosition:function(a,f,l,e){var h=this.axis,m=h.chart,d=e&&m.oldChartHeight||m.chartHeight;return{x:a?h.translate(f+l,null,null,e)+h.transB:h.left+h.offset+(h.opposite?(e&&m.oldChartWidth||m.chartWidth)-h.right-h.left:0),y:a?d-h.bottom+h.offset-(h.opposite?h.height:0):d-h.translate(f+l,null,null,e)-h.transB}},getLabelPosition:function(a, +f,l,e,h,m,d,c){var b=this.axis,k=b.transA,z=b.reversed,B=b.staggerLines,n=b.tickRotCorr||{x:0,y:0},x=h.y,u=e||b.reserveSpaceDefault?0:-b.labelOffset*("center"===b.labelAlign?.5:1);D(x)||(x=0===b.side?l.rotation?-8:-l.getBBox().height:2===b.side?n.y+8:Math.cos(l.rotation*r)*(n.y-l.getBBox(!1,0).height/2));a=a+h.x+u+n.x-(m&&e?m*k*(z?-1:1):0);f=f+x-(m&&!e?m*k*(z?1:-1):0);B&&(l=d/(c||1)%B,b.opposite&&(l=B-l-1),f+=b.labelOffset/B*l);return{x:a,y:Math.round(f)}},getMarkPath:function(a,f,l,e,h,m){return m.crispLine(["M", +a,f,"L",a+(h?0:-l),f+(h?l:0)],e)},renderGridLine:function(a,f,l){var e=this.axis,h=e.options,m=this.gridLine,d={},c=this.pos,b=this.type,k=e.tickmarkOffset,z=e.chart.renderer,B=b?b+"Grid":"grid",n=h[B+"LineWidth"],x=h[B+"LineColor"],h=h[B+"LineDashStyle"];m||(d.stroke=x,d["stroke-width"]=n,h&&(d.dashstyle=h),b||(d.zIndex=1),a&&(d.opacity=0),this.gridLine=m=z.path().attr(d).addClass("highcharts-"+(b?b+"-":"")+"grid-line").add(e.gridGroup));if(!a&&m&&(a=e.getPlotLinePath(c+k,m.strokeWidth()*l,a,!0)))m[this.isNew? +"attr":"animate"]({d:a,opacity:f})},renderMark:function(a,f,u){var e=this.axis,h=e.options,m=e.chart.renderer,d=this.type,c=d?d+"Tick":"tick",b=e.tickSize(c),k=this.mark,z=!k,B=a.x;a=a.y;var n=l(h[c+"Width"],!d&&e.isXAxis?1:0),h=h[c+"Color"];b&&(e.opposite&&(b[0]=-b[0]),z&&(this.mark=k=m.path().addClass("highcharts-"+(d?d+"-":"")+"tick").add(e.axisGroup),k.attr({stroke:h,"stroke-width":n})),k[z?"attr":"animate"]({d:this.getMarkPath(B,a,b[0],k.strokeWidth()*u,e.horiz,m),opacity:f}))},renderLabel:function(a, +f,u,e){var h=this.axis,m=h.horiz,d=h.options,c=this.label,b=d.labels,k=b.step,h=h.tickmarkOffset,z=!0,B=a.x;a=a.y;c&&p(B)&&(c.xy=a=this.getLabelPosition(B,a,c,m,b,h,e,k),this.isFirst&&!this.isLast&&!l(d.showFirstLabel,1)||this.isLast&&!this.isFirst&&!l(d.showLastLabel,1)?z=!1:!m||b.step||b.rotation||f||0===u||this.handleOverflow(a),k&&e%k&&(z=!1),z&&p(a.y)?(a.opacity=u,c[this.isNewLabel?"attr":"animate"](a),this.isNewLabel=!1):(c.attr("y",-9999),this.isNewLabel=!0))},render:function(a,f,u){var e= +this.axis,h=e.horiz,m=this.getPosition(h,this.pos,e.tickmarkOffset,f),d=m.x,c=m.y,e=h&&d===e.pos+e.len||!h&&c===e.pos?-1:1;u=l(u,1);this.isActive=!0;this.renderGridLine(f,u,e);this.renderMark(m,u,e);this.renderLabel(m,f,u,a);this.isNew=!1},destroy:function(){H(this,this.axis)}}})(M);var V=function(a){var E=a.addEvent,D=a.animObject,H=a.arrayMax,p=a.arrayMin,f=a.color,l=a.correctFloat,r=a.defaultOptions,n=a.defined,w=a.deg2rad,u=a.destroyObjectProperties,e=a.each,h=a.extend,m=a.fireEvent,d=a.format, +c=a.getMagnitude,b=a.grep,k=a.inArray,z=a.isArray,B=a.isNumber,I=a.isString,x=a.merge,K=a.normalizeTickInterval,t=a.objectEach,C=a.pick,N=a.removeEvent,q=a.splat,A=a.syncTimeout,F=a.Tick,G=function(){this.init.apply(this,arguments)};a.extend(G.prototype,{defaultOptions:{dateTimeLabelFormats:{millisecond:"%H:%M:%S.%L",second:"%H:%M:%S",minute:"%H:%M",hour:"%H:%M",day:"%e. %b",week:"%e. %b",month:"%b '%y",year:"%Y"},endOnTick:!1,labels:{enabled:!0,style:{color:"#666666",cursor:"default",fontSize:"11px"}, +x:0},maxPadding:.01,minorTickLength:2,minorTickPosition:"outside",minPadding:.01,startOfWeek:1,startOnTick:!1,tickLength:10,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",title:{align:"middle",style:{color:"#666666"}},type:"linear",minorGridLineColor:"#f2f2f2",minorGridLineWidth:1,minorTickColor:"#999999",lineColor:"#ccd6eb",lineWidth:1,gridLineColor:"#e6e6e6",tickColor:"#ccd6eb"},defaultYAxisOptions:{endOnTick:!0,tickPixelInterval:72,showLastLabel:!0,labels:{x:-8},maxPadding:.05, +minPadding:.05,startOnTick:!0,title:{rotation:270,text:"Values"},stackLabels:{allowOverlap:!1,enabled:!1,formatter:function(){return a.numberFormat(this.total,-1)},style:{fontSize:"11px",fontWeight:"bold",color:"#000000",textOutline:"1px contrast"}},gridLineWidth:1,lineWidth:0},defaultLeftAxisOptions:{labels:{x:-15},title:{rotation:270}},defaultRightAxisOptions:{labels:{x:15},title:{rotation:90}},defaultBottomAxisOptions:{labels:{autoRotation:[-45],x:0},title:{rotation:0}},defaultTopAxisOptions:{labels:{autoRotation:[-45], +x:0},title:{rotation:0}},init:function(a,b){var g=b.isX,v=this;v.chart=a;v.horiz=a.inverted&&!v.isZAxis?!g:g;v.isXAxis=g;v.coll=v.coll||(g?"xAxis":"yAxis");v.opposite=b.opposite;v.side=b.side||(v.horiz?v.opposite?0:2:v.opposite?1:3);v.setOptions(b);var c=this.options,d=c.type;v.labelFormatter=c.labels.formatter||v.defaultLabelFormatter;v.userOptions=b;v.minPixelPadding=0;v.reversed=c.reversed;v.visible=!1!==c.visible;v.zoomEnabled=!1!==c.zoomEnabled;v.hasNames="category"===d||!0===c.categories;v.categories= +c.categories||v.hasNames;v.names=v.names||[];v.plotLinesAndBandsGroups={};v.isLog="logarithmic"===d;v.isDatetimeAxis="datetime"===d;v.positiveValuesOnly=v.isLog&&!v.allowNegativeLog;v.isLinked=n(c.linkedTo);v.ticks={};v.labelEdge=[];v.minorTicks={};v.plotLinesAndBands=[];v.alternateBands={};v.len=0;v.minRange=v.userMinRange=c.minRange||c.maxZoom;v.range=c.range;v.offset=c.offset||0;v.stacks={};v.oldStacks={};v.stacksTouched=0;v.max=null;v.min=null;v.crosshair=C(c.crosshair,q(a.options.tooltip.crosshairs)[g? +0:1],!1);b=v.options.events;-1===k(v,a.axes)&&(g?a.axes.splice(a.xAxis.length,0,v):a.axes.push(v),a[v.coll].push(v));v.series=v.series||[];a.inverted&&!v.isZAxis&&g&&void 0===v.reversed&&(v.reversed=!0);t(b,function(a,g){E(v,g,a)});v.lin2log=c.linearToLogConverter||v.lin2log;v.isLog&&(v.val2lin=v.log2lin,v.lin2val=v.lin2log)},setOptions:function(a){this.options=x(this.defaultOptions,"yAxis"===this.coll&&this.defaultYAxisOptions,[this.defaultTopAxisOptions,this.defaultRightAxisOptions,this.defaultBottomAxisOptions, +this.defaultLeftAxisOptions][this.side],x(r[this.coll],a))},defaultLabelFormatter:function(){var g=this.axis,b=this.value,c=g.categories,k=this.dateTimeLabelFormat,e=r.lang,q=e.numericSymbols,e=e.numericSymbolMagnitude||1E3,h=q&&q.length,m,z=g.options.labels.format,g=g.isLog?Math.abs(b):g.tickInterval;if(z)m=d(z,this);else if(c)m=b;else if(k)m=a.dateFormat(k,b);else if(h&&1E3<=g)for(;h--&&void 0===m;)c=Math.pow(e,h+1),g>=c&&0===10*b%c&&null!==q[h]&&0!==b&&(m=a.numberFormat(b/c,-1)+q[h]);void 0=== +m&&(m=1E4<=Math.abs(b)?a.numberFormat(b,-1):a.numberFormat(b,-1,void 0,""));return m},getSeriesExtremes:function(){var a=this,v=a.chart;a.hasVisibleSeries=!1;a.dataMin=a.dataMax=a.threshold=null;a.softThreshold=!a.isXAxis;a.buildStacks&&a.buildStacks();e(a.series,function(g){if(g.visible||!v.options.chart.ignoreHiddenSeries){var c=g.options,d=c.threshold,k;a.hasVisibleSeries=!0;a.positiveValuesOnly&&0>=d&&(d=null);if(a.isXAxis)c=g.xData,c.length&&(g=p(c),k=H(c),B(g)||g instanceof Date||(c=b(c,B), +g=p(c)),a.dataMin=Math.min(C(a.dataMin,c[0],g),g),a.dataMax=Math.max(C(a.dataMax,c[0],k),k));else if(g.getExtremes(),k=g.dataMax,g=g.dataMin,n(g)&&n(k)&&(a.dataMin=Math.min(C(a.dataMin,g),g),a.dataMax=Math.max(C(a.dataMax,k),k)),n(d)&&(a.threshold=d),!c.softThreshold||a.positiveValuesOnly)a.softThreshold=!1}})},translate:function(a,b,c,d,k,e){var g=this.linkedParent||this,v=1,q=0,h=d?g.oldTransA:g.transA;d=d?g.oldMin:g.min;var m=g.minPixelPadding;k=(g.isOrdinal||g.isBroken||g.isLog&&k)&&g.lin2val; +h||(h=g.transA);c&&(v*=-1,q=g.len);g.reversed&&(v*=-1,q-=v*(g.sector||g.len));b?(a=(a*v+q-m)/h+d,k&&(a=g.lin2val(a))):(k&&(a=g.val2lin(a)),a=B(d)?v*(a-d)*h+q+v*m+(B(e)?h*e:0):void 0);return a},toPixels:function(a,b){return this.translate(a,!1,!this.horiz,null,!0)+(b?0:this.pos)},toValue:function(a,b){return this.translate(a-(b?0:this.pos),!0,!this.horiz,null,!0)},getPlotLinePath:function(a,b,c,d,k){var g=this.chart,v=this.left,q=this.top,e,h,m=c&&g.oldChartHeight||g.chartHeight,z=c&&g.oldChartWidth|| +g.chartWidth,A;e=this.transB;var t=function(a,g,b){if(ab)d?a=Math.min(Math.max(g,a),b):A=!0;return a};k=C(k,this.translate(a,null,null,c));a=c=Math.round(k+e);e=h=Math.round(m-k-e);B(k)?this.horiz?(e=q,h=m-this.bottom,a=c=t(a,v,v+this.width)):(a=v,c=z-this.right,e=h=t(e,q,q+this.height)):(A=!0,d=!1);return A&&!d?null:g.renderer.crispLine(["M",a,e,"L",c,h],b||1)},getLinearTickPositions:function(a,b,c){var g,v=l(Math.floor(b/a)*a);c=l(Math.ceil(c/a)*a);var d=[],k;l(v+a)===v&&(k=20);if(this.single)return[b]; +for(b=v;b<=c;){d.push(b);b=l(b+a,k);if(b===g)break;g=b}return d},getMinorTickInterval:function(){var a=this.options;return!0===a.minorTicks?C(a.minorTickInterval,"auto"):!1===a.minorTicks?null:a.minorTickInterval},getMinorTickPositions:function(){var a=this,b=a.options,c=a.tickPositions,d=a.minorTickInterval,k=[],q=a.pointRangePadding||0,h=a.min-q,q=a.max+q,m=q-h;if(m&&m/d=this.minRange,t=this.minRange,d=(t-c+b)/2,d=[b-d,C(a.min,b-d)],k&&(d[2]=this.isLog?this.log2lin(this.dataMin):this.dataMin),b=H(d),c=[b+t,C(a.max,b+t)],k&&(c[2]=this.isLog?this.log2lin(this.dataMax):this.dataMax),c=p(c),c-b=p?(r=p,f=0):b.dataMax<=p&&(w=p,x=0)),b.min= +C(N,r,b.dataMin),b.max=C(D,w,b.dataMax));q&&(b.positiveValuesOnly&&!g&&0>=Math.min(b.min,C(b.dataMin,b.min))&&a.error(10,1),b.min=l(h(b.min),15),b.max=l(h(b.max),15));b.range&&n(b.max)&&(b.userMin=b.min=N=Math.max(b.dataMin,b.minFromRange()),b.userMax=D=b.max,b.range=null);m(b,"foundExtremes");b.beforePadding&&b.beforePadding();b.adjustForMinRange();!(G||b.axisPointRange||b.usePercentage||t)&&n(b.min)&&n(b.max)&&(h=b.max-b.min)&&(!n(N)&&f&&(b.min-=h*f),!n(D)&&x&&(b.max+=h*x));B(k.softMin)&&!B(b.userMin)&& +(b.min=Math.min(b.min,k.softMin));B(k.softMax)&&!B(b.userMax)&&(b.max=Math.max(b.max,k.softMax));B(k.floor)&&(b.min=Math.max(b.min,k.floor));B(k.ceiling)&&(b.max=Math.min(b.max,k.ceiling));I&&n(b.dataMin)&&(p=p||0,!n(N)&&b.min=p?b.min=p:!n(D)&&b.max>p&&b.dataMax<=p&&(b.max=p));b.tickInterval=b.min===b.max||void 0===b.min||void 0===b.max?1:t&&!F&&u===b.linkedParent.options.tickPixelInterval?F=b.linkedParent.tickInterval:C(F,this.tickAmount?(b.max-b.min)/Math.max(this.tickAmount-1,1): +void 0,G?1:(b.max-b.min)*u/Math.max(b.len,u));A&&!g&&e(b.series,function(a){a.processData(b.min!==b.oldMin||b.max!==b.oldMax)});b.setAxisTranslation(!0);b.beforeSetTickPositions&&b.beforeSetTickPositions();b.postProcessTickInterval&&(b.tickInterval=b.postProcessTickInterval(b.tickInterval));b.pointRange&&!F&&(b.tickInterval=Math.max(b.pointRange,b.tickInterval));g=C(k.minTickInterval,b.isDatetimeAxis&&b.closestPointRange);!F&&b.tickIntervalb.tickInterval&&1E3b.max)),!!this.tickAmount));this.tickAmount||(b.tickInterval=b.unsquish());this.setTickPositions()},setTickPositions:function(){var a=this.options,b,c=a.tickPositions;b=this.getMinorTickInterval();var d=a.tickPositioner,k=a.startOnTick,q=a.endOnTick;this.tickmarkOffset=this.categories&&"between"===a.tickmarkPlacement&&1===this.tickInterval?.5:0;this.minorTickInterval="auto"===b&&this.tickInterval?this.tickInterval/ +5:b;this.single=this.min===this.max&&n(this.min)&&!this.tickAmount&&(parseInt(this.min,10)===this.min||!1!==a.allowDecimals);this.tickPositions=b=c&&c.slice();!b&&(b=this.isDatetimeAxis?this.getTimeTicks(this.normalizeTimeTickInterval(this.tickInterval,a.units),this.min,this.max,a.startOfWeek,this.ordinalPositions,this.closestPointRange,!0):this.isLog?this.getLogTickPositions(this.tickInterval,this.min,this.max):this.getLinearTickPositions(this.tickInterval,this.min,this.max),b.length>this.len&&(b= +[b[0],b.pop()],b[0]===b[1]&&(b.length=1)),this.tickPositions=b,d&&(d=d.apply(this,[this.min,this.max])))&&(this.tickPositions=b=d);this.paddedTicks=b.slice(0);this.trimTicks(b,k,q);this.isLinked||(this.single&&2>b.length&&(this.min-=.5,this.max+=.5),c||d||this.adjustTickAmount())},trimTicks:function(a,b,c){var g=a[0],d=a[a.length-1],k=this.minPointOffset||0;if(!this.isLinked){if(b&&-Infinity!==g)this.min=g;else for(;this.min-k>a[0];)a.shift();if(c)this.max=d;else for(;this.max+kb&&(this.finalTickAmt=b,b=5);this.tickAmount=b},adjustTickAmount:function(){var a=this.tickInterval,b=this.tickPositions,c=this.tickAmount,d=this.finalTickAmt,k=b&&b.length,q=C(this.threshold,this.softThreshold?0:null);if(this.hasData()){if(kc&&(this.tickInterval*= +2,this.setTickPositions());if(n(d)){for(a=c=b.length;a--;)(3===d&&1===a%2||2>=d&&0d&&(a=d)),n(c)&&(bd&&(b=d))),this.displayBtn=void 0!==a||void 0!==b,this.setExtremes(a,b,!1,void 0,{trigger:"zoom"});return!0},setAxisSize:function(){var b=this.chart,c=this.options,d=c.offsets||[0,0,0,0],k=this.horiz,q=this.width=Math.round(a.relativeLength(C(c.width,b.plotWidth-d[3]+d[1]),b.plotWidth)),e=this.height=Math.round(a.relativeLength(C(c.height, +b.plotHeight-d[0]+d[2]),b.plotHeight)),h=this.top=Math.round(a.relativeLength(C(c.top,b.plotTop+d[0]),b.plotHeight,b.plotTop)),c=this.left=Math.round(a.relativeLength(C(c.left,b.plotLeft+d[3]),b.plotWidth,b.plotLeft));this.bottom=b.chartHeight-e-h;this.right=b.chartWidth-q-c;this.len=Math.max(k?q:e,0);this.pos=k?c:h},getExtremes:function(){var a=this.isLog,b=this.lin2log;return{min:a?l(b(this.min)):this.min,max:a?l(b(this.max)):this.max,dataMin:this.dataMin,dataMax:this.dataMax,userMin:this.userMin, +userMax:this.userMax}},getThreshold:function(a){var b=this.isLog,g=this.lin2log,c=b?g(this.min):this.min,b=b?g(this.max):this.max;null===a?a=c:c>a?a=c:ba?"right":195a?"left":"center"},tickSize:function(a){var b=this.options,g=b[a+"Length"],c=C(b[a+"Width"],"tick"===a&&this.isXAxis?1:0);if(c&&g)return"inside"===b[a+"Position"]&&(g=-g),[g,c]},labelMetrics:function(){var a= +this.tickPositions&&this.tickPositions[0]||0;return this.chart.renderer.fontMetrics(this.options.labels.style&&this.options.labels.style.fontSize,this.ticks[a]&&this.ticks[a].label)},unsquish:function(){var a=this.options.labels,b=this.horiz,c=this.tickInterval,d=c,k=this.len/(((this.categories?1:0)+this.max-this.min)/c),q,h=a.rotation,m=this.labelMetrics(),z,A=Number.MAX_VALUE,t,x=function(a){a/=k||1;a=1=a)z=x(Math.abs(m.h/Math.sin(w*a))),b=z+Math.abs(a/360),b(c.step||0)&&!c.rotation&&(this.staggerLines||1)*this.len/d||!b&&(c.style&&parseInt(c.style.width,10)||k&&k-a.spacing[3]|| +.33*a.chartWidth)},renderUnsquish:function(){var a=this.chart,b=a.renderer,c=this.tickPositions,d=this.ticks,k=this.options.labels,q=this.horiz,h=this.getSlotWidth(),m=Math.max(1,Math.round(h-2*(k.padding||5))),z={},A=this.labelMetrics(),t=k.style&&k.style.textOverflow,f,F=0,l,B;I(k.rotation)||(z.rotation=k.rotation||0);e(c,function(a){(a=d[a])&&a.labelLength>F&&(F=a.labelLength)});this.maxLabelLength=F;if(this.autoRotation)F>m&&F>A.h?z.rotation=this.labelRotation:this.labelRotation=0;else if(h&& +(f={width:m+"px"},!t))for(f.textOverflow="clip",l=c.length;!q&&l--;)if(B=c[l],m=d[B].label)m.styles&&"ellipsis"===m.styles.textOverflow?m.css({textOverflow:"clip"}):d[B].labelLength>h&&m.css({width:h+"px"}),m.getBBox().height>this.len/c.length-(A.h-A.f)&&(m.specCss={textOverflow:"ellipsis"});z.rotation&&(f={width:(F>.5*a.chartHeight?.33*a.chartHeight:a.chartHeight)+"px"},t||(f.textOverflow="ellipsis"));if(this.labelAlign=k.align||this.autoLabelAlign(this.labelRotation))z.align=this.labelAlign;e(c, +function(a){var b=(a=d[a])&&a.label;b&&(b.attr(z),f&&b.css(x(f,b.specCss)),delete b.specCss,a.rotation=z.rotation)});this.tickRotCorr=b.rotCorr(A.b,this.labelRotation||0,0!==this.side)},hasData:function(){return this.hasVisibleSeries||n(this.min)&&n(this.max)&&this.tickPositions&&0=this.min&&a<=this.max)g[a]||(g[a]=new F(this,a)),d&&g[a].isNew&&g[a].render(b,!0,.1),g[a].render(b)},render:function(){var b= +this,c=b.chart,d=b.options,k=b.isLog,q=b.lin2log,h=b.isLinked,m=b.tickPositions,z=b.axisTitle,x=b.ticks,f=b.minorTicks,l=b.alternateBands,C=d.stackLabels,n=d.alternateGridColor,u=b.tickmarkOffset,G=b.axisLine,p=b.showAxis,I=D(c.renderer.globalAnimation),r,w;b.labelEdge.length=0;b.overlap=!1;e([x,f,l],function(a){t(a,function(a){a.isActive=!1})});if(b.hasData()||h)b.minorTickInterval&&!b.categories&&e(b.getMinorTickPositions(),function(a){b.renderMinorTick(a)}),m.length&&(e(m,function(a,c){b.renderTick(a, +c)}),u&&(0===b.min||b.single)&&(x[-1]||(x[-1]=new F(b,-1,null,!0)),x[-1].render(-1))),n&&e(m,function(d,g){w=void 0!==m[g+1]?m[g+1]+u:b.max-u;0===g%2&&d=h.second?0:C*Math.floor(x.getMilliseconds()/C));if(t>=h.second)x[D.hcSetSeconds](t>=h.minute? +0:C*Math.floor(x.getSeconds()/C));if(t>=h.minute)x[D.hcSetMinutes](t>=h.hour?0:C*Math.floor(x[D.hcGetMinutes]()/C));if(t>=h.hour)x[D.hcSetHours](t>=h.day?0:C*Math.floor(x[D.hcGetHours]()/C));if(t>=h.day)x[D.hcSetDate](t>=h.month?1:C*Math.floor(x[D.hcGetDate]()/C));t>=h.month&&(x[D.hcSetMonth](t>=h.year?0:C*Math.floor(x[D.hcGetMonth]()/C)),n=x[D.hcGetFullYear]());if(t>=h.year)x[D.hcSetFullYear](n-n%C);if(t===h.week)x[D.hcSetDate](x[D.hcGetDate]()-x[D.hcGetDay]()+e(b,1));n=x[D.hcGetFullYear]();b=x[D.hcGetMonth](); +var A=x[D.hcGetDate](),F=x[D.hcGetHours]();d=x.getTime();D.hcHasTimeZone&&(q=(!B||!!D.hcGetTimezoneOffset)&&(c-d>4*h.month||w(d)!==w(c)),N=w(x),x=new D(d+N));B=x.getTime();for(d=1;Bk.length&&l(k,function(a){0===a%18E5&&"000000000"===H("%H%M%S%L",a)&&(m[a]="day")})}k.info=r(a,{higherRanks:m,totalRange:t*C});return k}; +E.prototype.normalizeTimeTickInterval=function(a,d){var c=d||[["millisecond",[1,2,5,10,20,25,50,100,200,500]],["second",[1,2,5,10,15,30]],["minute",[1,2,5,10,15,30]],["hour",[1,2,3,4,6,8,12]],["day",[1,2]],["week",[1,2]],["month",[1,2,3,4,6]],["year",null]];d=c[c.length-1];var b=h[d[0]],k=d[1],e;for(e=0;er&&(!w||z<=n)&&void 0!==z&&d.push(z),z>n&& +(B=!0),z=k;else r=h(r),n=h(n),a=w?this.getMinorTickInterval():l.tickInterval,a=f("auto"===a?null:a,this._minorAutoInterval,l.tickPixelInterval/(w?5:1)*(n-r)/((w?e/this.tickPositions.length:e)||1)),a=p(a,null,D(a)),d=H(this.getLinearTickPositions(a,r,n),m),w||(this._minorAutoInterval=a/5);w||(this.tickInterval=a);return d};E.prototype.log2lin=function(a){return Math.log(a)/Math.LN10};E.prototype.lin2log=function(a){return Math.pow(10,a)}})(M);(function(a,E){var D=a.arrayMax,H=a.arrayMin,p=a.defined, +f=a.destroyObjectProperties,l=a.each,r=a.erase,n=a.merge,w=a.pick;a.PlotLineOrBand=function(a,e){this.axis=a;e&&(this.options=e,this.id=e.id)};a.PlotLineOrBand.prototype={render:function(){var f=this,e=f.axis,h=e.horiz,m=f.options,d=m.label,c=f.label,b=m.to,k=m.from,z=m.value,l=p(k)&&p(b),r=p(z),x=f.svgElem,K=!x,t=[],C=m.color,N=w(m.zIndex,0),q=m.events,t={"class":"highcharts-plot-"+(l?"band ":"line ")+(m.className||"")},A={},F=e.chart.renderer,G=l?"bands":"lines",g=e.log2lin;e.isLog&&(k=g(k),b=g(b), +z=g(z));r?(t={stroke:C,"stroke-width":m.width},m.dashStyle&&(t.dashstyle=m.dashStyle)):l&&(C&&(t.fill=C),m.borderWidth&&(t.stroke=m.borderColor,t["stroke-width"]=m.borderWidth));A.zIndex=N;G+="-"+N;(C=e.plotLinesAndBandsGroups[G])||(e.plotLinesAndBandsGroups[G]=C=F.g("plot-"+G).attr(A).add());K&&(f.svgElem=x=F.path().attr(t).add(C));if(r)t=e.getPlotLinePath(z,x.strokeWidth());else if(l)t=e.getPlotBandPath(k,b,m);else return;K&&t&&t.length?(x.attr({d:t}),q&&a.objectEach(q,function(a,b){x.on(b,function(a){q[b].apply(f, +[a])})})):x&&(t?(x.show(),x.animate({d:t})):(x.hide(),c&&(f.label=c=c.destroy())));d&&p(d.text)&&t&&t.length&&0this.max&& +e>this.max;if(m&&h)for(a&&(k=m.toString()===h.toString(),b=0),a=0;aA-h?A:A-h);else if(z)k[a]=Math.max(e,g+h+d>c?g:g+h);else return!1},C=function(a,c,d,g){var e;gc-b?e=!1:k[a]=gc-d/2?c-d-2:g-d/2;return e},p=function(a){var b=f;f=x;x=b;m=a},q=function(){!1!==t.apply(0,f)?!1!==C.apply(0,x)||m||(p(!0),q()):m?k.x=k.y=0:(p(!0),q())};(c.inverted||1q&&(h=!1);a=(e.series&& +e.series.yAxis&&e.series.yAxis.pos)+(e.plotY||0);a-=b.plotTop;c.push({target:e.isHeader?b.plotHeight+l:a,rank:e.isHeader?1:0,size:z.tt.getBBox().height+1,point:e,x:q,tt:t})}});this.cleanSplit();a.distribute(c,b.plotHeight+l);D(c,function(a){var c=a.point,d=c.series;a.tt.attr({visibility:void 0===a.pos?"hidden":"inherit",x:h||c.isHeader?a.x:c.plotX+b.plotLeft+n(f.distance,16),y:a.pos+b.plotTop,anchorX:c.isHeader?c.plotX+b.plotLeft:c.plotX+d.xAxis.pos,anchorY:c.isHeader?a.pos+b.plotTop-15:c.plotY+d.yAxis.pos})})}, +updatePosition:function(a){var e=this.chart,d=this.getLabel(),d=(this.options.positioner||this.getPosition).call(this,d.width,d.height,a);this.move(Math.round(d.x),Math.round(d.y||0),a.plotX+e.plotLeft,a.plotY+e.plotTop)},getDateFormat:function(a,m,d,c){var b=E("%m-%d %H:%M:%S.%L",m),k,h,f={millisecond:15,second:12,minute:9,hour:6,day:3},l="millisecond";for(h in e){if(a===e.week&&+E("%w",m)===d&&"00:00:00.000"===b.substr(6)){h="week";break}if(e[h]>a){h=l;break}if(f[h]&&b.substr(f[h])!=="01-01 00:00:00.000".substr(f[h]))break; +"week"!==h&&(l=h)}h&&(k=c[h]);return k},getXDateFormat:function(a,e,d){e=e.dateTimeLabelFormats;var c=d&&d.closestPointRange;return(c?this.getDateFormat(c,a.x,d.options.startOfWeek,e):e.day)||e.year},tooltipFooterHeaderFormatter:function(a,e){e=e?"footer":"header";var d=a.series,c=d.tooltipOptions,b=c.xDateFormat,k=d.xAxis,h=k&&"datetime"===k.options.type&&f(a.key),m=c[e+"Format"];h&&!b&&(b=this.getXDateFormat(a,c,k));h&&b&&D(a.point&&a.point.tooltipDateKeys||["key"],function(a){m=m.replace("{point."+ +a+"}","{point."+a+":"+b+"}")});return p(m,{point:a,series:d})},bodyFormatter:function(a){return l(a,function(a){var d=a.series.tooltipOptions;return(d[(a.point.formatPrefix||"point")+"Formatter"]||a.point.tooltipFormatter).call(a.point,d[(a.point.formatPrefix||"point")+"Format"])})}}})(M);(function(a){var E=a.addEvent,D=a.attr,H=a.charts,p=a.color,f=a.css,l=a.defined,r=a.each,n=a.extend,w=a.find,u=a.fireEvent,e=a.isObject,h=a.offset,m=a.pick,d=a.splat,c=a.Tooltip;a.Pointer=function(a,c){this.init(a, +c)};a.Pointer.prototype={init:function(a,d){this.options=d;this.chart=a;this.runChartClick=d.chart.events&&!!d.chart.events.click;this.pinchDown=[];this.lastValidTouch={};c&&(a.tooltip=new c(a,d.tooltip),this.followTouchMove=m(d.tooltip.followTouchMove,!0));this.setDOMEvents()},zoomOption:function(a){var b=this.chart,c=b.options.chart,d=c.zoomType||"",b=b.inverted;/touch/.test(a.type)&&(d=m(c.pinchType,d));this.zoomX=a=/x/.test(d);this.zoomY=d=/y/.test(d);this.zoomHor=a&&!b||d&&b;this.zoomVert=d&& +!b||a&&b;this.hasZoom=a||d},normalize:function(a,c){var b;b=a.touches?a.touches.length?a.touches.item(0):a.changedTouches[0]:a;c||(this.chartPosition=c=h(this.chart.container));return n(a,{chartX:Math.round(b.pageX-c.left),chartY:Math.round(b.pageY-c.top)})},getCoordinates:function(a){var b={xAxis:[],yAxis:[]};r(this.chart.axes,function(c){b[c.isXAxis?"xAxis":"yAxis"].push({axis:c,value:c.toValue(a[c.horiz?"chartX":"chartY"])})});return b},findNearestKDPoint:function(a,c,d){var b;r(a,function(a){var k= +!(a.noSharedTooltip&&c)&&0>a.options.findNearestPointBy.indexOf("y");a=a.searchPoint(d,k);if((k=e(a,!0))&&!(k=!e(b,!0)))var k=b.distX-a.distX,h=b.dist-a.dist,m=(a.series.group&&a.series.group.zIndex)-(b.series.group&&b.series.group.zIndex),k=0<(0!==k&&c?k:0!==h?h:0!==m?m:b.series.index>a.series.index?-1:1);k&&(b=a)});return b},getPointFromEvent:function(a){a=a.target;for(var b;a&&!b;)b=a.point,a=a.parentNode;return b},getChartCoordinatesFromPoint:function(a,c){var b=a.series,d=b.xAxis,b=b.yAxis,k= +m(a.clientX,a.plotX);if(d&&b)return c?{chartX:d.len+d.pos-k,chartY:b.len+b.pos-a.plotY}:{chartX:k+d.pos,chartY:a.plotY+b.pos}},getHoverData:function(b,c,d,h,f,l,n){var k,z=[],x=n&&n.isBoosting;h=!(!h||!b);n=c&&!c.stickyTracking?[c]:a.grep(d,function(a){return a.visible&&!(!f&&a.directTouch)&&m(a.options.enableMouseTracking,!0)&&a.stickyTracking});c=(k=h?b:this.findNearestKDPoint(n,f,l))&&k.series;k&&(f&&!c.noSharedTooltip?(n=a.grep(d,function(a){return a.visible&&!(!f&&a.directTouch)&&m(a.options.enableMouseTracking, +!0)&&!a.noSharedTooltip}),r(n,function(a){var b=w(a.points,function(a){return a.x===k.x&&!a.isNull});e(b)&&(x&&(b=a.getPoint(b)),z.push(b))})):z.push(k));return{hoverPoint:k,hoverSeries:c,hoverPoints:z}},runPointActions:function(b,c){var d=this.chart,k=d.tooltip&&d.tooltip.options.enabled?d.tooltip:void 0,e=k?k.shared:!1,h=c||d.hoverPoint,f=h&&h.series||d.hoverSeries,f=this.getHoverData(h,f,d.series,!!c||f&&f.directTouch&&this.isDirectTouch,e,b,{isBoosting:d.isBoosting}),l,h=f.hoverPoint;l=f.hoverPoints; +c=(f=f.hoverSeries)&&f.tooltipOptions.followPointer;e=e&&f&&!f.noSharedTooltip;if(h&&(h!==d.hoverPoint||k&&k.isHidden)){r(d.hoverPoints||[],function(b){-1===a.inArray(b,l)&&b.setState()});r(l||[],function(a){a.setState("hover")});if(d.hoverSeries!==f)f.onMouseOver();d.hoverPoint&&d.hoverPoint.firePointEvent("mouseOut");if(!h.series)return;h.firePointEvent("mouseOver");d.hoverPoints=l;d.hoverPoint=h;k&&k.refresh(e?l:h,b)}else c&&k&&!k.isHidden&&(h=k.getAnchor([{}],b),k.updatePosition({plotX:h[0],plotY:h[1]})); +this.unDocMouseMove||(this.unDocMouseMove=E(d.container.ownerDocument,"mousemove",function(b){var c=H[a.hoverChartIndex];if(c)c.pointer.onDocumentMouseMove(b)}));r(d.axes,function(c){var d=m(c.crosshair.snap,!0),k=d?a.find(l,function(a){return a.series[c.coll]===c}):void 0;k||!d?c.drawCrosshair(b,k):c.hideCrosshair()})},reset:function(a,c){var b=this.chart,k=b.hoverSeries,e=b.hoverPoint,h=b.hoverPoints,m=b.tooltip,f=m&&m.shared?h:e;a&&f&&r(d(f),function(b){b.series.isCartesian&&void 0===b.plotX&& +(a=!1)});if(a)m&&f&&(m.refresh(f),e&&(e.setState(e.state,!0),r(b.axes,function(a){a.crosshair&&a.drawCrosshair(null,e)})));else{if(e)e.onMouseOut();h&&r(h,function(a){a.setState()});if(k)k.onMouseOut();m&&m.hide(c);this.unDocMouseMove&&(this.unDocMouseMove=this.unDocMouseMove());r(b.axes,function(a){a.hideCrosshair()});this.hoverX=b.hoverPoints=b.hoverPoint=null}},scaleGroups:function(a,c){var b=this.chart,d;r(b.series,function(k){d=a||k.getPlotBox();k.xAxis&&k.xAxis.zoomEnabled&&k.group&&(k.group.attr(d), +k.markerGroup&&(k.markerGroup.attr(d),k.markerGroup.clip(c?b.clipRect:null)),k.dataLabelsGroup&&k.dataLabelsGroup.attr(d))});b.clipRect.attr(c||b.clipBox)},dragStart:function(a){var b=this.chart;b.mouseIsDown=a.type;b.cancelClick=!1;b.mouseDownX=this.mouseDownX=a.chartX;b.mouseDownY=this.mouseDownY=a.chartY},drag:function(a){var b=this.chart,c=b.options.chart,d=a.chartX,e=a.chartY,h=this.zoomHor,m=this.zoomVert,f=b.plotLeft,l=b.plotTop,n=b.plotWidth,q=b.plotHeight,A,F=this.selectionMarker,G=this.mouseDownX, +g=this.mouseDownY,v=c.panKey&&a[c.panKey+"Key"];F&&F.touch||(df+n&&(d=f+n),el+q&&(e=l+q),this.hasDragged=Math.sqrt(Math.pow(G-d,2)+Math.pow(g-e,2)),10u.max&&(f=u.max-x,v=!0);v?(F-=.8*(F-m[b][0]),q||(g-=.8*(g-m[b][1])),l()):m[b]=[F,g];C||(h[b]=w-r,h[n]=x);h=C?1/t:t;e[n]=x;e[b]=f;p[C?a?"scaleY":"scaleX":"scale"+k]=t;p["translate"+k]=h*r+(F-h*A)},pinch:function(a){var n=this,r=n.chart,u=n.pinchDown,e=a.touches,h=e.length,m=n.lastValidTouch, +d=n.hasZoom,c=n.selectionMarker,b={},k=1===h&&(n.inClass(a.target,"highcharts-tracker")&&r.runTrackerClick||n.runChartClick),z={};1c-6&&h(q||c.spacingBox.width-2*t-d.x)&&(this.itemX=t,this.itemY+=F+this.lastLineHeight+A,this.lastLineHeight=0);this.maxItemWidth=Math.max(this.maxItemWidth,m);this.lastItemY=F+this.itemY+A;this.lastLineHeight=Math.max(b,this.lastLineHeight);a._legendItemPos=[this.itemX,this.itemY];e?this.itemX+=m:(this.itemY+=F+b+A,this.lastLineHeight=b);this.offsetWidth=q||Math.max((e?this.itemX-t-(a.checkbox?0:p):m)+t,this.offsetWidth)}, +getAllItems:function(){var a=[];f(this.chart.series,function(c){var b=c&&c.options;c&&w(b.showInLegend,p(b.linkedTo)?!1:void 0,!0)&&(a=a.concat(c.legendItems||("point"===b.legendType?c.data:c)))});return a},getAlignment:function(){var a=this.options;return a.floating?"":a.align.charAt(0)+a.verticalAlign.charAt(0)+a.layout.charAt(0)},adjustMargins:function(a,c){var b=this.chart,d=this.options,e=this.getAlignment();e&&f([/(lth|ct|rth)/,/(rtv|rm|rbv)/,/(rbh|cb|lbh)/,/(lbv|lm|ltv)/],function(k,h){k.test(e)&& +!p(a[h])&&(b[r[h]]=Math.max(b[r[h]],b.legend[(h+1)%2?"legendHeight":"legendWidth"]+[1,-1,-1,1][h]*d[h%2?"x":"y"]+w(d.margin,12)+c[h]+(0===h?b.titleOffset+b.options.title.margin:0)))})},render:function(){var a=this,c=a.chart,b=c.renderer,k=a.group,h,m,l,x,p=a.box,t=a.options,C=a.padding;a.itemX=C;a.itemY=a.initialItemY;a.offsetWidth=0;a.lastItemY=0;k||(a.group=k=b.g("legend").attr({zIndex:7}).add(),a.contentGroup=b.g().attr({zIndex:1}).add(k),a.scrollGroup=b.g().add(a.contentGroup));a.renderTitle(); +h=a.getAllItems();e(h,function(a,b){return(a.options&&a.options.legendIndex||0)-(b.options&&b.options.legendIndex||0)});t.reversed&&h.reverse();a.allItems=h;a.display=m=!!h.length;a.lastLineHeight=0;f(h,function(b){a.renderItem(b)});l=(t.width||a.offsetWidth)+C;x=a.lastItemY+a.lastLineHeight+a.titleHeight;x=a.handleOverflow(x);x+=C;p||(a.box=p=b.rect().addClass("highcharts-legend-box").attr({r:t.borderRadius}).add(k),p.isNew=!0);p.attr({stroke:t.borderColor,"stroke-width":t.borderWidth||0,fill:t.backgroundColor|| +"none"}).shadow(t.shadow);0b&&!1!==t.enabled?(this.clipHeight=l=Math.max(b-20-this.titleHeight-m,0),this.currentPage=w(this.currentPage,1),this.fullHeight=a,f(G,function(a,b){var c=a._legendItemPos[1],d=Math.round(a.legendItem.getBBox().height),g=A.length;if(!g||c-A[g-1]>l&&(F||c)!==A[g-1])A.push(F||c),g++;a.pageIx=g-1;F&&(G[b-1].pageIx=g-1);b===G.length-1&&c+d-A[g-1]>l&&(A.push(c),a.pageIx=g);c!==F&&(F=c)}),n||(n=c.clipRect=d.clipRect(0,m,9999,0),c.contentGroup.clip(n)),g(l),q||(this.nav=q=d.g().attr({zIndex:1}).add(this.group), +this.up=d.symbol("triangle",0,0,r,r).on("click",function(){c.scroll(-1,p)}).add(q),this.pager=d.text("",15,10).addClass("highcharts-legend-navigation").css(t.style).add(q),this.down=d.symbol("triangle-down",0,0,r,r).on("click",function(){c.scroll(1,p)}).add(q)),c.scroll(0),a=b):q&&(g(),this.nav=q.destroy(),this.scrollGroup.attr({translateY:1}),this.clipHeight=0);return a},scroll:function(a,c){var b=this.pages,d=b.length;a=this.currentPage+a;var e=this.clipHeight,h=this.options.navigation,f=this.pager, +m=this.padding;a>d&&(a=d);0b&&(f=typeof a[0],"string"===f?e.name=a[0]:"number"===f&&(e.x=a[0]),k++);l=d.value;)d=h[++f];d&&d.color&&!this.options.color&&(this.color=d.color);return d},destroy:function(){var a=this.series.chart,h=a.hoverPoints,f;a.pointCount--;h&&(this.setState(),p(h,this),h.length||(a.hoverPoints=null));if(this===a.hoverPoint)this.onMouseOut();if(this.graphic||this.dataLabel)u(this),this.destroyElements();this.legendItem&&a.legend.destroyItem(this);for(f in this)this[f]=null},destroyElements:function(){for(var a=["graphic","dataLabel", +"dataLabelUpper","connector","shadowGroup"],h,f=6;f--;)h=a[f],this[h]&&(this[h]=this[h].destroy())},getLabelConfig:function(){return{x:this.category,y:this.y,color:this.color,colorIndex:this.colorIndex,key:this.name||this.category,series:this.series,point:this,percentage:this.percentage,total:this.total||this.stackTotal}},tooltipFormatter:function(a){var e=this.series,f=e.tooltipOptions,d=w(f.valueDecimals,""),c=f.valuePrefix||"",b=f.valueSuffix||"";D(e.pointArrayMap||["y"],function(e){e="{point."+ +e;if(c||b)a=a.replace(e+"}",c+e+"}"+b);a=a.replace(e+"}",e+":,."+d+"f}")});return l(a,{point:this,series:this.series})},firePointEvent:function(a,h,m){var d=this,c=this.series.options;(c.point.events[a]||d.options&&d.options.events&&d.options.events[a])&&this.importEvents();"click"===a&&c.allowPointSelect&&(m=function(a){d.select&&d.select(null,a.ctrlKey||a.metaKey||a.shiftKey)});f(this,a,h,m)},visible:!0}})(M);(function(a){var E=a.addEvent,D=a.animObject,H=a.arrayMax,p=a.arrayMin,f=a.correctFloat, +l=a.Date,r=a.defaultOptions,n=a.defaultPlotOptions,w=a.defined,u=a.each,e=a.erase,h=a.extend,m=a.fireEvent,d=a.grep,c=a.isArray,b=a.isNumber,k=a.isString,z=a.merge,B=a.objectEach,I=a.pick,x=a.removeEvent,K=a.splat,t=a.SVGElement,C=a.syncTimeout,N=a.win;a.Series=a.seriesType("line",null,{lineWidth:2,allowPointSelect:!1,showCheckbox:!1,animation:{duration:1E3},events:{},marker:{lineWidth:0,lineColor:"#ffffff",radius:4,states:{hover:{animation:{duration:50},enabled:!0,radiusPlus:2,lineWidthPlus:1},select:{fillColor:"#cccccc", +lineColor:"#000000",lineWidth:2}}},point:{events:{}},dataLabels:{align:"center",formatter:function(){return null===this.y?"":a.numberFormat(this.y,-1)},style:{fontSize:"11px",fontWeight:"bold",color:"contrast",textOutline:"1px contrast"},verticalAlign:"bottom",x:0,y:0,padding:5},cropThreshold:300,pointRange:0,softThreshold:!0,states:{hover:{animation:{duration:50},lineWidthPlus:1,marker:{},halo:{size:10,opacity:.25}},select:{marker:{}}},stickyTracking:!0,turboThreshold:1E3,findNearestPointBy:"x"}, +{isCartesian:!0,pointClass:a.Point,sorted:!0,requireSorting:!0,directTouch:!1,axisTypes:["xAxis","yAxis"],colorCounter:0,parallelArrays:["x","y"],coll:"series",init:function(a,b){var c=this,d,g=a.series,e;c.chart=a;c.options=b=c.setOptions(b);c.linkedSeries=[];c.bindAxes();h(c,{name:b.name,state:"",visible:!1!==b.visible,selected:!0===b.selected});d=b.events;B(d,function(a,b){E(c,b,a)});if(d&&d.click||b.point&&b.point.events&&b.point.events.click||b.allowPointSelect)a.runTrackerClick=!0;c.getColor(); +c.getSymbol();u(c.parallelArrays,function(a){c[a+"Data"]=[]});c.setData(b.data,!1);c.isCartesian&&(a.hasCartesianSeries=!0);g.length&&(e=g[g.length-1]);c._i=I(e&&e._i,-1)+1;a.orderSeries(this.insert(g))},insert:function(a){var c=this.options.index,d;if(b(c)){for(d=a.length;d--;)if(c>=I(a[d].options.index,a[d]._i)){a.splice(d+1,0,this);break}-1===d&&a.unshift(this);d+=1}else a.push(this);return I(d,a.length-1)},bindAxes:function(){var b=this,c=b.options,d=b.chart,e;u(b.axisTypes||[],function(g){u(d[g], +function(a){e=a.options;if(c[g]===e.index||void 0!==c[g]&&c[g]===e.id||void 0===c[g]&&0===e.index)b.insert(a.series),b[g]=a,a.isDirty=!0});b[g]||b.optionalAxis===g||a.error(18,!0)})},updateParallelArrays:function(a,c){var d=a.series,e=arguments,g=b(c)?function(b){var g="y"===b&&d.toYData?d.toYData(a):a[b];d[b+"Data"][c]=g}:function(a){Array.prototype[c].apply(d[a+"Data"],Array.prototype.slice.call(e,2))};u(d.parallelArrays,g)},autoIncrement:function(){var b=this.options,c=this.xIncrement,d,e=b.pointIntervalUnit, +g=0,c=I(c,b.pointStart,0);this.pointInterval=d=I(this.pointInterval,b.pointInterval,1);e&&(b=new l(c),"day"===e?b=+b[l.hcSetDate](b[l.hcGetDate]()+d):"month"===e?b=+b[l.hcSetMonth](b[l.hcGetMonth]()+d):"year"===e&&(b=+b[l.hcSetFullYear](b[l.hcGetFullYear]()+d)),l.hcHasTimeZone&&(g=a.getTZOffset(b)-a.getTZOffset(c)),d=b-c+g);this.xIncrement=c+d;return c},setOptions:function(a){var b=this.chart,c=b.options,d=c.plotOptions,g=(b.userOptions||{}).plotOptions||{},e=d[this.type];this.userOptions=a;b=z(e, +d.series,a);this.tooltipOptions=z(r.tooltip,r.plotOptions.series&&r.plotOptions.series.tooltip,r.plotOptions[this.type].tooltip,c.tooltip.userOptions,d.series&&d.series.tooltip,d[this.type].tooltip,a.tooltip);this.stickyTracking=I(a.stickyTracking,g[this.type]&&g[this.type].stickyTracking,g.series&&g.series.stickyTracking,this.tooltipOptions.shared&&!this.noSharedTooltip?!0:b.stickyTracking);null===e.marker&&delete b.marker;this.zoneAxis=b.zoneAxis;a=this.zones=(b.zones||[]).slice();!b.negativeColor&& +!b.negativeFillColor||b.zones||a.push({value:b[this.zoneAxis+"Threshold"]||b.threshold||0,className:"highcharts-negative",color:b.negativeColor,fillColor:b.negativeFillColor});a.length&&w(a[a.length-1].value)&&a.push({color:this.color,fillColor:this.fillColor});return b},getCyclic:function(a,b,c){var d,g=this.chart,e=this.userOptions,k=a+"Index",h=a+"Counter",q=c?c.length:I(g.options.chart[a+"Count"],g[a+"Count"]);b||(d=I(e[k],e["_"+k]),w(d)||(g.series.length||(g[h]=0),e["_"+k]=d=g[h]%q,g[h]+=1), +c&&(b=c[d]));void 0!==d&&(this[k]=d);this[a]=b},getColor:function(){this.options.colorByPoint?this.options.color=null:this.getCyclic("color",this.options.color||n[this.type].color,this.chart.options.colors)},getSymbol:function(){this.getCyclic("symbol",this.options.marker.symbol,this.chart.options.symbols)},drawLegendSymbol:a.LegendSymbolMixin.drawLineMarker,setData:function(d,e,h,f){var g=this,q=g.points,m=q&&q.length||0,l,A=g.options,t=g.chart,x=null,n=g.xAxis,p=A.turboThreshold,z=this.xData,F= +this.yData,C=(l=g.pointArrayMap)&&l.length;d=d||[];l=d.length;e=I(e,!0);if(!1!==f&&l&&m===l&&!g.cropped&&!g.hasGroupedData&&g.visible)u(d,function(a,b){q[b].update&&a!==A.data[b]&&q[b].update(a,!1,null,!1)});else{g.xIncrement=null;g.colorCounter=0;u(this.parallelArrays,function(a){g[a+"Data"].length=0});if(p&&l>p){for(h=0;null===x&&hf||this.forceCrop))if(c[e-1]z)c=[],d=[];else if(c[0]z)g=this.cropData(this.xData,this.yData,p,z),c=g.xData,d=g.yData,g=g.start,k=!0;for(f=c.length|| +1;--f;)e=x?m(c[f])-m(c[f-1]):c[f]-c[f-1],0e&&n&&(a.error(15),n=!1);this.cropped=k;this.cropStart=g;this.processedXData=c;this.processedYData=d;this.closestPointRange=h},cropData:function(a,b,c,d){var g=a.length,e=0,k=g,h=I(this.cropShoulder,1),f;for(f=0;f=c){e=Math.max(0,f-h);break}for(c=f;cd){k=c+h;break}return{xData:a.slice(e,k),yData:b.slice(e,k),start:e,end:k}},generatePoints:function(){var a=this.options,b=a.data,c=this.data,d,g=this.processedXData, +e=this.processedYData,k=this.pointClass,h=g.length,f=this.cropStart||0,m,l=this.hasGroupedData,a=a.keys,t,x=[],n;c||l||(c=[],c.length=b.length,c=this.data=c);a&&l&&(this.options.keys=!1);for(n=0;n=f&&(e[n-1]||l)<=q,m&&l)if(m=t.length)for(;m--;)"number"===typeof t[m]&&(g[h++]=t[m]);else g[h++]=t;this.dataMin= +p(g);this.dataMax=H(g)},translate:function(){this.processedXData||this.processData();this.generatePoints();var a=this.options,c=a.stacking,d=this.xAxis,e=d.categories,g=this.yAxis,k=this.points,h=k.length,m=!!this.modifyValue,l=a.pointPlacement,t="between"===l||b(l),n=a.threshold,x=a.startFromThreshold?n:0,p,z,C,r,u=Number.MAX_VALUE;"between"===l&&(l=.5);b(l)&&(l*=I(a.pointRange||d.pointRange));for(a=0;a=K&&(B.isNull=!0);B.plotX=p=f(Math.min(Math.max(-1E5,d.translate(N,0,0,0,1,l,"flags"===this.type)),1E5));c&&this.visible&&!B.isNull&&D&&D[N]&&(r=this.getStackIndicator(r,N,this.index),E=D[N],K=E.points[r.key],z=K[0],K=K[1],z===x&&r.key===D[N].base&&(z=I(n,g.min)),g.positiveValuesOnly&&0>=z&&(z=null),B.total=B.stackTotal=E.total,B.percentage=E.total&&B.y/E.total*100,B.stackY=K,E.setOffset(this.pointXOffset||0,this.barW||0));B.yBottom=w(z)?g.translate(z,0,1,0,1): +null;m&&(K=this.modifyValue(K,B));B.plotY=z="number"===typeof K&&Infinity!==K?Math.min(Math.max(-1E5,g.translate(K,0,1,0,1)),1E5):void 0;B.isInside=void 0!==z&&0<=z&&z<=g.len&&0<=p&&p<=d.len;B.clientX=t?f(d.translate(N,0,0,0,1,l)):p;B.negative=B.y<(n||0);B.category=e&&void 0!==e[B.x]?e[B.x]:B.x;B.isNull||(void 0!==C&&(u=Math.min(u,Math.abs(p-C))),C=p);B.zone=this.zones.length&&B.getZone()}this.closestPointRangePx=u},getValidPoints:function(a,b){var c=this.chart;return d(a||this.points||[],function(a){return b&& +!c.isInsidePlot(a.plotX,a.plotY,c.inverted)?!1:!a.isNull})},setClip:function(a){var b=this.chart,c=this.options,d=b.renderer,g=b.inverted,e=this.clipBox,k=e||b.clipBox,h=this.sharedClipKey||["_sharedClip",a&&a.duration,a&&a.easing,k.height,c.xAxis,c.yAxis].join(),f=b[h],q=b[h+"m"];f||(a&&(k.width=0,g&&(k.x=b.plotSizeX),b[h+"m"]=q=d.clipRect(g?b.plotSizeX+99:-99,g?-b.plotLeft:-b.plotTop,99,g?b.chartWidth:b.chartHeight)),b[h]=f=d.clipRect(k),f.count={length:0});a&&!f.count[this.index]&&(f.count[this.index]= +!0,f.count.length+=1);!1!==c.clip&&(this.group.clip(a||e?f:b.clipRect),this.markerGroup.clip(q),this.sharedClipKey=h);a||(f.count[this.index]&&(delete f.count[this.index],--f.count.length),0===f.count.length&&h&&b[h]&&(e||(b[h]=b[h].destroy()),b[h+"m"]&&(b[h+"m"]=b[h+"m"].destroy())))},animate:function(a){var b=this.chart,c=D(this.options.animation),d;a?this.setClip(c):(d=this.sharedClipKey,(a=b[d])&&a.animate({width:b.plotSizeX,x:0},c),b[d+"m"]&&b[d+"m"].animate({width:b.plotSizeX+99,x:0},c),this.animate= +null)},afterAnimate:function(){this.setClip();m(this,"afterAnimate");this.finishedAnimating=!0},drawPoints:function(){var a=this.points,b=this.chart,c,d,g,e,k=this.options.marker,h,f,l,m=this[this.specialGroup]||this.markerGroup,t,n=I(k.enabled,this.xAxis.isRadial?!0:null,this.closestPointRangePx>=2*k.radius);if(!1!==k.enabled||this._hasPointMarkers)for(c=0;ce&&b.shadow));k&&(k.startX=c.xMap,k.isArea=c.isArea)})}, +applyZones:function(){var a=this,b=this.chart,c=b.renderer,d=this.zones,e,k,h=this.clips||[],f,m=this.graph,l=this.area,t=Math.max(b.chartWidth,b.chartHeight),n=this[(this.zoneAxis||"y")+"Axis"],x,p,z=b.inverted,C,r,w,B,K=!1;d.length&&(m||l)&&n&&void 0!==n.min&&(p=n.reversed,C=n.horiz,m&&m.hide(),l&&l.hide(),x=n.getExtremes(),u(d,function(d,g){e=p?C?b.plotWidth:0:C?0:n.toPixels(x.min);e=Math.min(Math.max(I(k,e),0),t);k=Math.min(Math.max(Math.round(n.toPixels(I(d.value,x.max),!0)),0),t);K&&(e=k=n.toPixels(x.max)); +r=Math.abs(e-k);w=Math.min(e,k);B=Math.max(e,k);n.isXAxis?(f={x:z?B:w,y:0,width:r,height:t},C||(f.x=b.plotHeight-f.x)):(f={x:0,y:z?B:w,width:t,height:r},C&&(f.y=b.plotWidth-f.y));z&&c.isVML&&(f=n.isXAxis?{x:0,y:p?w:B,height:f.width,width:b.chartWidth}:{x:f.y-b.plotLeft-b.spacingBox.x,y:0,width:f.height,height:b.chartHeight});h[g]?h[g].animate(f):(h[g]=c.clipRect(f),m&&a["zone-graph-"+g].clip(h[g]),l&&a["zone-area-"+g].clip(h[g]));K=d.value>x.max}),this.clips=h)},invertGroups:function(a){function b(){u(["group", +"markerGroup"],function(b){c[b]&&(d.renderer.isVML&&c[b].attr({width:c.yAxis.len,height:c.xAxis.len}),c[b].width=c.yAxis.len,c[b].height=c.xAxis.len,c[b].invert(a))})}var c=this,d=c.chart,e;c.xAxis&&(e=E(d,"resize",b),E(c,"destroy",e),b(a),c.invertGroups=b)},plotGroup:function(a,b,c,d,e){var g=this[a],k=!g;k&&(this[a]=g=this.chart.renderer.g().attr({zIndex:d||.1}).add(e));g.addClass("highcharts-"+b+" highcharts-series-"+this.index+" highcharts-"+this.type+"-series "+(w(this.colorIndex)?"highcharts-color-"+ +this.colorIndex+" ":"")+(this.options.className||"")+(g.hasClass("highcharts-tracker")?" highcharts-tracker":""),!0);g.attr({visibility:c})[k?"attr":"animate"](this.getPlotBox());return g},getPlotBox:function(){var a=this.chart,b=this.xAxis,c=this.yAxis;a.inverted&&(b=c,c=this.xAxis);return{translateX:b?b.left:a.plotLeft,translateY:c?c.top:a.plotTop,scaleX:1,scaleY:1}},render:function(){var a=this,b=a.chart,c,d=a.options,e=!!a.animate&&b.renderer.isSVG&&D(d.animation).duration,k=a.visible?"inherit": +"hidden",h=d.zIndex,f=a.hasRendered,m=b.seriesGroup,l=b.inverted;c=a.plotGroup("group","series",k,h,m);a.markerGroup=a.plotGroup("markerGroup","markers",k,h,m);e&&a.animate(!0);c.inverted=a.isCartesian?l:!1;a.drawGraph&&(a.drawGraph(),a.applyZones());a.drawDataLabels&&a.drawDataLabels();a.visible&&a.drawPoints();a.drawTracker&&!1!==a.options.enableMouseTracking&&a.drawTracker();a.invertGroups(l);!1===d.clip||a.sharedClipKey||f||c.clip(b.clipRect);e&&a.animate();f||(a.animationTimeout=C(function(){a.afterAnimate()}, +e));a.isDirty=!1;a.hasRendered=!0},redraw:function(){var a=this.chart,b=this.isDirty||this.isDirtyData,c=this.group,d=this.xAxis,e=this.yAxis;c&&(a.inverted&&c.attr({width:a.plotWidth,height:a.plotHeight}),c.animate({translateX:I(d&&d.left,a.plotLeft),translateY:I(e&&e.top,a.plotTop)}));this.translate();this.render();b&&delete this.kdTree},kdAxisArray:["clientX","plotY"],searchPoint:function(a,b){var c=this.xAxis,d=this.yAxis,e=this.chart.inverted;return this.searchKDTree({clientX:e?c.len-a.chartY+ +c.pos:a.chartX-c.pos,plotY:e?d.len-a.chartX+d.pos:a.chartY-d.pos},b)},buildKDTree:function(){function a(c,d,e){var g,k;if(k=c&&c.length)return g=b.kdAxisArray[d%e],c.sort(function(a,b){return a[g]-b[g]}),k=Math.floor(k/2),{point:c[k],left:a(c.slice(0,k),d+1,e),right:a(c.slice(k+1),d+1,e)}}this.buildingKdTree=!0;var b=this,c=-1l?"left":"right";t=0>l?"right":"left";b[q]&&(q=c(a,b[q],g+1,f),n=q[h]x;)q--;this.updateParallelArrays(m,"splice",q,0,0);this.updateParallelArrays(m,q);g&&m.name&&(g[x]=m.name);l.splice(q,0,a);n&&(this.data.splice(q,0,null),this.processData());"point"===e.legendType&&this.generatePoints();c&&(h[0]&&h[0].remove?h[0].remove(!1):(h.shift(),this.updateParallelArrays(m,"shift"),l.shift()));this.isDirtyData=this.isDirty=!0;b&&f.redraw(d)},removePoint:function(a,b,c){var d=this,e=d.data,h=e[a],f=d.points,g=d.chart,l=function(){f&&f.length===e.length&& +f.splice(a,1);e.splice(a,1);d.options.data.splice(a,1);d.updateParallelArrays(h||{series:d},"splice",a,1);h&&h.destroy();d.isDirty=!0;d.isDirtyData=!0;b&&g.redraw()};x(c,g);b=k(b,!0);h?h.firePointEvent("remove",null,l):l()},remove:function(a,b,c){function d(){e.destroy();h.isDirtyLegend=h.isDirtyBox=!0;h.linkSeries();k(a,!0)&&h.redraw(b)}var e=this,h=e.chart;!1!==c?u(e,"remove",null,d):d()},update:function(a,b){var d=this,e=d.chart,h=d.userOptions,f=d.oldType||d.type,l=a.type||h.type||e.options.chart.type, +g=I[f].prototype,m,n=["group","markerGroup","dataLabelsGroup"],t=["navigatorSeries","baseSeries"],x=d.finishedAnimating&&{animation:!1};if(Object.keys&&"data"===Object.keys(a).toString())return this.setData(a.data,b);t=n.concat(t);r(t,function(a){t[a]=d[a];delete d[a]});a=c(h,x,{index:d.index,pointStart:d.xData[0]},{data:d.options.data},a);d.remove(!1,null,!1);for(m in g)d[m]=void 0;w(d,I[l||f].prototype);r(t,function(a){d[a]=t[a]});d.init(e,a);a.zIndex!==h.zIndex&&r(n,function(b){d[b]&&d[b].attr({zIndex:a.zIndex})}); +d.oldType=f;e.linkSeries();k(b,!0)&&e.redraw(!1)}});w(H.prototype,{update:function(a,b){var d=this.chart;a=d.options[this.coll][this.options.index]=c(this.userOptions,a);this.destroy(!0);this.init(d,w(a,{events:void 0}));d.isDirtyBox=!0;k(b,!0)&&d.redraw()},remove:function(a){for(var b=this.chart,c=this.coll,e=this.series,h=e.length;h--;)e[h]&&e[h].remove(!1);n(b.axes,this);n(b[c],this);d(b.options[c])?b.options[c].splice(this.options.index,1):delete b.options[c];r(b[c],function(a,b){a.options.index= +b});this.destroy();b.isDirtyBox=!0;k(a,!0)&&b.redraw()},setTitle:function(a,b){this.update({title:a},b)},setCategories:function(a,b){this.update({categories:a},b)}})})(M);(function(a){var E=a.color,D=a.each,H=a.map,p=a.pick,f=a.Series,l=a.seriesType;l("area","line",{softThreshold:!1,threshold:0},{singleStacks:!1,getStackPoints:function(f){var l=[],r=[],u=this.xAxis,e=this.yAxis,h=e.stacks[this.stackKey],m={},d=this.index,c=e.series,b=c.length,k,z=p(e.options.reversedStacks,!0)?1:-1,B;f=f||this.points; +if(this.options.stacking){for(B=0;Ba&&w>l?(w=Math.max(a,l),e=2*l-w):wp&&e>l?(e=Math.max(p,l),w=2*l-e):e=Math.abs(h)&&.5a.closestPointRange*a.xAxis.transA,d=a.borderWidth=r(f.borderWidth,d?0:1),c=a.yAxis,b=f.threshold,k=a.translatedThreshold=c.getThreshold(b),l=r(f.minPointLength,5),p=a.getColumnMetrics(),u=p.width,x=a.barW=Math.max(u,1+2*d),w=a.pointXOffset=p.offset;h.inverted&&(k-=.5);f.pointPadding&&(x=Math.ceil(x));n.prototype.translate.apply(a);H(a.points,function(d){var e=r(d.yBottom,k),f=999+Math.abs(e),f=Math.min(Math.max(-f,d.plotY),c.len+f),m=d.plotX+w,n=x,t=Math.min(f,e),p,g=Math.max(f,e)-t;l&& +Math.abs(g)l?e-l:k-(p?l:0));d.barX=m;d.pointWidth=u;d.tooltipPos=h.inverted?[c.len+c.pos-h.plotLeft-f,a.xAxis.len-m-n/2,g]:[m+n/2,f+c.pos-h.plotTop,g];d.shapeType="rect";d.shapeArgs=a.crispCol.apply(a,d.isNull?[m,k,n,0]:[m,t,n,g])})},getSymbol:a.noop,drawLegendSymbol:a.LegendSymbolMixin.drawRectangle,drawGraph:function(){this.group[this.dense?"addClass":"removeClass"]("highcharts-dense-data")}, +pointAttribs:function(a,f){var e=this.options,d,c=this.pointAttrToOptions||{};d=c.stroke||"borderColor";var b=c["stroke-width"]||"borderWidth",k=a&&a.color||this.color,h=a&&a[d]||e[d]||this.color||k,n=a&&a[b]||e[b]||this[b]||0,c=e.dashStyle;a&&this.zones.length&&(k=a.getZone(),k=a.options.color||k&&k.color||this.color);f&&(a=l(e.states[f],a.options.states&&a.options.states[f]||{}),f=a.brightness,k=a.color||void 0!==f&&D(k).brighten(a.brightness).get()||k,h=a[d]||h,n=a[b]||n,c=a.dashStyle||c);d={fill:k, +stroke:h,"stroke-width":n};c&&(d.dashstyle=c);return d},drawPoints:function(){var a=this,h=this.chart,m=a.options,d=h.renderer,c=m.animationLimit||250,b;H(a.points,function(e){var k=e.graphic;if(f(e.plotY)&&null!==e.y){b=e.shapeArgs;if(k)k[h.pointCounte;++e)h=w[e],a=2>e||2===e&&/%$/.test(h),w[e]=p(h,[n,l,u,w[2]][e])+(a?r:0);w[3]>w[2]&&(w[3]=w[2]);return w},getStartAndEndRadians:function(a,l){a=D(a)?a:0;l=D(l)&&l>a&&360>l-a?l:a+360;return{start:E*(a+-90),end:E*(l+-90)}}}})(M);(function(a){var E=a.addEvent,D=a.CenteredSeriesMixin,H=a.defined,p=a.each,f=a.extend,l=D.getStartAndEndRadians,r=a.inArray,n=a.noop,w=a.pick,u=a.Point,e=a.Series,h=a.seriesType,m=a.setAnimation;h("pie","line",{center:[null,null],clip:!1,colorByPoint:!0,dataLabels:{distance:30, +enabled:!0,formatter:function(){return this.point.isNull?void 0:this.point.name},x:0},ignoreHiddenPoint:!0,legendType:"point",marker:null,size:null,showInLegend:!1,slicedOffset:10,stickyTracking:!1,tooltip:{followPointer:!0},borderColor:"#ffffff",borderWidth:1,states:{hover:{brightness:.1,shadow:!1}}},{isCartesian:!1,requireSorting:!1,directTouch:!0,noSharedTooltip:!0,trackerGroups:["group","dataLabelsGroup"],axisTypes:[],pointAttribs:a.seriesTypes.column.prototype.pointAttribs,animate:function(a){var c= +this,b=c.points,d=c.startAngleRad;a||(p(b,function(a){var b=a.graphic,e=a.shapeArgs;b&&(b.attr({r:a.startR||c.center[3]/2,start:d,end:d}),b.animate({r:e.r,start:e.start,end:e.end},c.options.animation))}),c.animate=null)},updateTotals:function(){var a,c=0,b=this.points,e=b.length,f,h=this.options.ignoreHiddenPoint;for(a=0;a1.5*Math.PI?m-=2*Math.PI:m<-Math.PI/2&&(m+=2*Math.PI);G.slicedTranslation={translateX:Math.round(Math.cos(m)*d),translateY:Math.round(Math.sin(m)*d)};h=Math.cos(m)*a[2]/ +2;u=Math.sin(m)*a[2]/2;G.tooltipPos=[a[0]+.7*h,a[1]+.7*u];G.half=m<-Math.PI/2||m>Math.PI/2?1:0;G.angle=m;f=Math.min(e,G.labelDistance/5);G.labelPos=[a[0]+h+Math.cos(m)*G.labelDistance,a[1]+u+Math.sin(m)*G.labelDistance,a[0]+h+Math.cos(m)*f,a[1]+u+Math.sin(m)*f,a[0]+h,a[1]+u,0>G.labelDistance?"center":G.half?"right":"left",m]}},drawGraph:null,drawPoints:function(){var a=this,c=a.chart.renderer,b,e,h,l,m=a.options.shadow;m&&!a.shadowGroup&&(a.shadowGroup=c.g("shadow").add(a.group));p(a.points,function(d){e= +d.graphic;if(d.isNull)e&&(d.graphic=e.destroy());else{l=d.shapeArgs;b=d.getTranslate();var k=d.shadowGroup;m&&!k&&(k=d.shadowGroup=c.g("shadow").add(a.shadowGroup));k&&k.attr(b);h=a.pointAttribs(d,d.selected&&"select");e?e.setRadialReference(a.center).attr(h).animate(f(l,b)):(d.graphic=e=c[d.shapeType](l).setRadialReference(a.center).attr(b).add(a.group),d.visible||e.attr({visibility:"hidden"}),e.attr(h).attr({"stroke-linejoin":"round"}).shadow(m,k));e.addClass(d.getClassName())}})},searchPoint:n, +sortByAngle:function(a,c){a.sort(function(a,d){return void 0!==a.angle&&(d.angle-a.angle)*c})},drawLegendSymbol:a.LegendSymbolMixin.drawRectangle,getCenter:D.getCenter,getSymbol:n},{init:function(){u.prototype.init.apply(this,arguments);var a=this,c;a.name=w(a.name,"Slice");c=function(b){a.slice("select"===b.type)};E(a,"select",c);E(a,"unselect",c);return a},isValid:function(){return a.isNumber(this.y,!0)&&0<=this.y},setVisible:function(a,c){var b=this,d=b.series,e=d.chart,f=d.options.ignoreHiddenPoint; +c=w(c,f);a!==b.visible&&(b.visible=b.options.visible=a=void 0===a?!b.visible:a,d.options.data[r(b,d.data)]=b.options,p(["graphic","dataLabel","connector","shadowGroup"],function(c){if(b[c])b[c][a?"show":"hide"](!0)}),b.legendItem&&e.legend.colorizeItem(b,a),a||"hover"!==b.state||b.setState(""),f&&(d.isDirty=!0),c&&e.redraw())},slice:function(a,c,b){var d=this.series;m(b,d.chart);w(c,!0);this.sliced=this.options.sliced=H(a)?a:!this.sliced;d.options.data[r(this,d.data)]=this.options;this.graphic.animate(this.getTranslate()); +this.shadowGroup&&this.shadowGroup.animate(this.getTranslate())},getTranslate:function(){return this.sliced?this.slicedTranslation:{translateX:0,translateY:0}},haloPath:function(a){var c=this.shapeArgs;return this.sliced||!this.visible?[]:this.series.chart.renderer.symbols.arc(c.x,c.y,c.r+a,c.r+a,{innerR:this.shapeArgs.r-1,start:c.start,end:c.end})}})})(M);(function(a){var E=a.addEvent,D=a.arrayMax,H=a.defined,p=a.each,f=a.extend,l=a.format,r=a.map,n=a.merge,w=a.noop,u=a.pick,e=a.relativeLength,h= +a.Series,m=a.seriesTypes,d=a.stableSort;a.distribute=function(a,b){function c(a,b){return a.target-b.target}var e,f=!0,h=a,l=[],m;m=0;for(e=a.length;e--;)m+=a[e].size;if(m>b){d(a,function(a,b){return(b.rank||0)-(a.rank||0)});for(m=e=0;m<=b;)m+=a[e].size,e++;l=a.splice(e-1,a.length)}d(a,c);for(a=r(a,function(a){return{size:a.size,targets:[a.target],align:u(a.align,.5)}});f;){for(e=a.length;e--;)f=a[e],m=(Math.min.apply(0,f.targets)+Math.max.apply(0,f.targets))/2,f.pos=Math.min(Math.max(0,m-f.size* +f.align),b-f.size);e=a.length;for(f=!1;e--;)0a[e].pos&&(a[e-1].size+=a[e].size,a[e-1].targets=a[e-1].targets.concat(a[e].targets),a[e-1].align=.5,a[e-1].pos+a[e-1].size>b&&(a[e-1].pos=b-a[e-1].size),a.splice(e,1),f=!0)}e=0;p(a,function(a){var b=0;p(a.targets,function(){h[e].pos=a.pos+b;b+=h[e].size;e++})});h.push.apply(h,l);d(h,c)};h.prototype.drawDataLabels=function(){function c(a,b){var c=b.filter;return c?(b=c.operator,a=a[c.property],c=c.value,"\x3e"===b&&a>c||"\x3c"=== +b&&a=c||"\x3c\x3d"===b&&a<=c||"\x3d\x3d"===b&&a==c||"\x3d\x3d\x3d"===b&&a===c?!0:!1):!0}var b=this,d=b.options,e=d.dataLabels,f=b.points,h,m,r=b.hasRendered||0,t,w,D=u(e.defer,!!d.animation),q=b.chart.renderer;if(e.enabled||b._hasPointLabels)b.dlProcessOptions&&b.dlProcessOptions(e),w=b.plotGroup("dataLabelsGroup","data-labels",D&&!r?"hidden":"visible",e.zIndex||6),D&&(w.attr({opacity:+r}),r||E(b,"afterAnimate",function(){b.visible&&w.show(!0);w[d.animation?"animate":"attr"]({opacity:1}, +{duration:200})})),m=e,p(f,function(f){var k,p=f.dataLabel,g,x,r=f.connector,z=!p,C;h=f.dlOptions||f.options&&f.options.dataLabels;(k=u(h&&h.enabled,m.enabled)&&!f.isNull)&&(k=!0===c(f,h||e));k&&(e=n(m,h),g=f.getLabelConfig(),C=e[f.formatPrefix+"Format"]||e.format,t=H(C)?l(C,g):(e[f.formatPrefix+"Formatter"]||e.formatter).call(g,e),C=e.style,g=e.rotation,C.color=u(e.color,C.color,b.color,"#000000"),"contrast"===C.color&&(f.contrastColor=q.getContrast(f.color||b.color),C.color=e.inside||0>u(f.labelDistance, +e.distance)||d.stacking?f.contrastColor:"#000000"),d.cursor&&(C.cursor=d.cursor),x={fill:e.backgroundColor,stroke:e.borderColor,"stroke-width":e.borderWidth,r:e.borderRadius||0,rotation:g,padding:e.padding,zIndex:1},a.objectEach(x,function(a,b){void 0===a&&delete x[b]}));!p||k&&H(t)?k&&H(t)&&(p?x.text=t:(p=f.dataLabel=g?q.text(t,0,-9999).addClass("highcharts-data-label"):q.label(t,0,-9999,e.shape,null,null,e.useHTML,null,"data-label"),p.addClass(" highcharts-data-label-color-"+f.colorIndex+" "+(e.className|| +"")+(e.useHTML?"highcharts-tracker":""))),p.attr(x),p.css(C).shadow(e.shadow),p.added||p.add(w),b.alignDataLabel(f,p,e,null,z)):(f.dataLabel=p=p.destroy(),r&&(f.connector=r.destroy()))})};h.prototype.alignDataLabel=function(a,b,d,e,h){var c=this.chart,k=c.inverted,l=u(a.dlBox&&a.dlBox.centerX,a.plotX,-9999),m=u(a.plotY,-9999),n=b.getBBox(),p,q=d.rotation,r=d.align,w=this.visible&&(a.series.forceDL||c.isInsidePlot(l,Math.round(m),k)||e&&c.isInsidePlot(l,k?e.x+1:e.y+e.height-1,k)),z="justify"===u(d.overflow, +"justify");if(w&&(p=d.style.fontSize,p=c.renderer.fontMetrics(p,b).b,e=f({x:k?this.yAxis.len-m:l,y:Math.round(k?this.xAxis.len-l:m),width:0,height:0},e),f(d,{width:n.width,height:n.height}),q?(z=!1,l=c.renderer.rotCorr(p,q),l={x:e.x+d.x+e.width/2+l.x,y:e.y+d.y+{top:0,middle:.5,bottom:1}[d.verticalAlign]*e.height},b[h?"attr":"animate"](l).attr({align:r}),m=(q+720)%360,m=180m,"left"===r?l.y-=m?n.height:0:"center"===r?(l.x-=n.width/2,l.y-=n.height/2):"right"===r&&(l.x-=n.width,l.y-=m?0:n.height)): +(b.align(d,null,e),l=b.alignAttr),z?a.isLabelJustified=this.justifyDataLabel(b,d,l,n,e,h):u(d.crop,!0)&&(w=c.isInsidePlot(l.x,l.y)&&c.isInsidePlot(l.x+n.width,l.y+n.height)),d.shape&&!q))b[h?"attr":"animate"]({anchorX:k?c.plotWidth-a.plotY:a.plotX,anchorY:k?c.plotHeight-a.plotX:a.plotY});w||(b.attr({y:-9999}),b.placed=!1)};h.prototype.justifyDataLabel=function(a,b,d,e,f,h){var c=this.chart,k=b.align,l=b.verticalAlign,m,n,p=a.box?0:a.padding||0;m=d.x+p;0>m&&("right"===k?b.align="left":b.x=-m,n=!0); +m=d.x+e.width-p;m>c.plotWidth&&("left"===k?b.align="right":b.x=c.plotWidth-m,n=!0);m=d.y+p;0>m&&("bottom"===l?b.verticalAlign="top":b.y=-m,n=!0);m=d.y+e.height-p;m>c.plotHeight&&("top"===l?b.verticalAlign="bottom":b.y=c.plotHeight-m,n=!0);n&&(a.placed=!h,a.align(b,null,f));return n};m.pie&&(m.pie.prototype.drawDataLabels=function(){var c=this,b=c.data,d,e=c.chart,f=c.options.dataLabels,l=u(f.connectorPadding,10),m=u(f.connectorWidth,1),n=e.plotWidth,t=e.plotHeight,r,w=c.center,q=w[2]/2,A=w[1],F,G, +g,v,E=[[],[]],L,P,J,M,y=[0,0,0,0];c.visible&&(f.enabled||c._hasPointLabels)&&(p(b,function(a){a.dataLabel&&a.visible&&a.dataLabel.shortened&&(a.dataLabel.attr({width:"auto"}).css({width:"auto",textOverflow:"clip"}),a.dataLabel.shortened=!1)}),h.prototype.drawDataLabels.apply(c),p(b,function(a){a.dataLabel&&a.visible&&(E[a.half].push(a),a.dataLabel._pos=null)}),p(E,function(b,h){var k,m,x=b.length,r=[],z;if(x)for(c.sortByAngle(b,h-.5),0d.bottom-2?k:P,h,d),F._attr={visibility:J,align:g[6]},F._pos={x:L+f.x+({left:l,right:-l}[g[6]]||0),y:P+f.y-10},g.x=L,g.y=P,u(f.crop,!0)&&(G=F.getBBox().width,k=null,L-Gn-l&&(k=Math.round(L+G-n+l),y[1]=Math.max(k,y[1])),0>P-v/2?y[0]=Math.max(Math.round(-P+v/2),y[0]):P+v/2>t&&(y[2]=Math.max(Math.round(P+v/2-t),y[2])),F.sideOverflow=k)}),0===D(y)||this.verifyDataLabelOverflow(y))&&(this.placeDataLabels(), +m&&p(this.points,function(a){var b;r=a.connector;if((F=a.dataLabel)&&F._pos&&a.visible&&0u(this.translatedThreshold,k.yAxis.len)),p=u(d.inside,!!this.options.stacking);l&&(e=n(l),0>e.y&&(e.height+=e.y,e.y=0),l=e.y+e.height-k.yAxis.len,0a+c||e+hb+d||f+lthis.pointCount))},pan:function(a,b){var c=this,d=c.hoverPoints,e;d&&r(d,function(a){a.setState()});r("xy"===b?[1,0]:[1],function(b){b= +c[b?"xAxis":"yAxis"][0];var d=b.horiz,f=a[d?"chartX":"chartY"],d=d?"mouseDownX":"mouseDownY",h=c[d],g=(b.pointRange||0)/2,k=b.getExtremes(),l=b.toValue(h-f,!0)+g,m=b.toValue(h+b.len-f,!0)-g,n=m=l(n.minWidth,0)&&this.chartHeight>=l(n.minHeight,0)}).call(this)&&f.push(a._id)};E.prototype.currentOptions=function(l){function n(e,h,l,d){var c;a.objectEach(e,function(a,e){if(!d&&-1 li.first padding-left: 0px h2 font-size: 150% - -//#subtitle -// color: lighten($brown, 30%) -// font-style: italic -// font-weight: normal -// margin-top: 0px -// padding-left: 1em -// padding-top: 0px - h3 font-size: 120% diff --git a/app/assets/stylesheets/predictions.sass b/app/assets/stylesheets/predictions.sass new file mode 100644 index 000000000..b9496ebc5 --- /dev/null +++ b/app/assets/stylesheets/predictions.sass @@ -0,0 +1,11 @@ +.predictions + .metric + height: 180px + border: #000000 2px + background: $white + margin: 4px + strong + font-size: 250% + font-align: center + h3 + diff --git a/app/controllers/crops_controller.rb b/app/controllers/crops_controller.rb index 3df1a7ca7..b62177e5d 100644 --- a/app/controllers/crops_controller.rb +++ b/app/controllers/crops_controller.rb @@ -51,11 +51,7 @@ class CropsController < ApplicationController def show @crop = Crop.includes(:scientific_names, plantings: :photos).find(params[:id]) @posts = @crop.posts.order(created_at: :desc).paginate(page: params[:page]) - - respond_to do |format| - format.html # show.html.haml - format.json { render json: @crop.to_json(crop_json_fields) } - end + respond_with(@crop) end def new @@ -106,6 +102,14 @@ class CropsController < ApplicationController respond_with @crop end + def sunniness + @crop = Crop.find(params[:crop_id]) + render json: Planting.where(crop: @crop) + .where.not(sunniness: nil) + .where.not(sunniness: '') + .group(:sunniness).count(:id) + end + private def notifier diff --git a/app/views/crops/_predictions.html.haml b/app/views/crops/_predictions.html.haml index dbde9c783..343f4f6bd 100644 --- a/app/views/crops/_predictions.html.haml +++ b/app/views/crops/_predictions.html.haml @@ -1,29 +1,34 @@ - unless crop.perennial.nil? - %p - #{crop.name} is - - if crop.perennial == true - = link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do - a perennial crop - (living more than two years) - - elsif crop.perennial == false - = link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do - an annual crop - (living and reproducing in a single year or less) + .predictions + .row + %p + #{crop.name} is + - if crop.perennial == true + = link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do + a perennial crop + (living more than two years) + - elsif crop.perennial == false + = link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do + an annual crop + (living and reproducing in a single year or less) -- if crop.annual? && crop.median_lifespan.present? - %p - Median lifespan of #{crop.name} plants is - %strong= crop.median_lifespan - days + .row + - if crop.annual? && crop.median_lifespan.present? + .metric.col-md-3.col-xs-5 + %h3 Median lifespan + %strong= crop.median_lifespan + %span days -- if crop.median_days_to_first_harvest.present? - %p - First harvest expected - %strong= crop.median_days_to_first_harvest - days after planting + - if crop.median_days_to_first_harvest.present? + .metric.col-md-3.col-xs-5 + %h3 First harvest expected + %strong= crop.median_days_to_first_harvest + %span days + after planting -- if crop.annual? && crop.median_days_to_last_harvest.present? - %p - Last harvest expected - %strong= crop.median_days_to_last_harvest - days after planting + - if crop.annual? && crop.median_days_to_last_harvest.present? + .metric.col-md-3.col-xs-5 + %h3 Last harvest expected + %strong= crop.median_days_to_last_harvest + %span days + after planting diff --git a/app/views/crops/show.html.haml b/app/views/crops/show.html.haml index 2789d61ef..cf401681e 100644 --- a/app/views/crops/show.html.haml +++ b/app/views/crops/show.html.haml @@ -8,6 +8,9 @@ = tag("meta", property: "og:url", content: request.original_url) = tag("meta", property: "og:site_name", content: ENV['GROWSTUFF_SITE_NAME']) +- content_for :scripts do + = javascript_include_tag "charts" + = render partial: 'approval_status_message', locals: { crop: @crop } - if @crop.approved? @@ -27,11 +30,6 @@ = display_seed_availability(@current_member, @crop) = link_to "View your seeds", seeds_by_owner_path(owner: current_member.slug) - %h2 Predictions - = render 'predictions', crop: @crop - - %p= render 'crops/photos', crop: @crop - %h2 - if !@crop.plantings.empty? = @crop.name.titleize @@ -41,19 +39,26 @@ - else Nobody is growing this yet. You could be the first! - %h2 - Sunniness Chart + .row + .col-md-3 + %h2 Sunniness Chart + = pie_chart crop_sunniness_path(@crop), legend: "bottom" - #sunchart + .col-md-9 + %h2 Predictions + = render 'predictions', crop: @crop - %h2 - Crop Map - %p - Only plantings by members who have set their locations are shown on this map. - - if current_member && current_member.location.blank? - = link_to "Set your location.", edit_member_registration_path + .row + .col-md-12 + %h2 Photos + %p= render 'crops/photos', crop: @crop - #cropmap + %h2 Crop Map + %p + Only plantings by members who have set their locations are shown on this map. + - if current_member && current_member.location.blank? + = link_to "Set your location.", edit_member_registration_path + #cropmap %a{ name: 'posts' } %h2 What people are saying about #{@crop.name.pluralize} diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index d7e05a282..b7d570330 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,5 +1,6 @@ !!! 5 %html{ lang: "en", prefix: "og: http://ogp.me/ns#" } + = yield :scripts = render partial: "layouts/meta" %body = render partial: "layouts/header" diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 4b828e80c..60484d4a4 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -12,3 +12,4 @@ Rails.application.config.assets.paths << Rails.root.join('node_modules') # application.js, application.css, and all non-JS/CSS in the app/assets # folder are already added. # Rails.application.config.assets.precompile += %w( admin.js admin.css ) +Rails.application.config.assets.precompile += %w(charts.js) diff --git a/config/routes.rb b/config/routes.rb index 013fee209..d22049d55 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -50,6 +50,7 @@ Rails.application.routes.draw do get 'crops/search' => 'crops#search', as: 'crops_search' resources :crops do get 'photos' => 'photos#index' + get 'sunniness' => 'crops#sunniness' end resources :comments From ac16347f0d4bf0ee42d4f0a7ec15c7fc19cb0b42 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 7 Jan 2018 18:23:42 +1300 Subject: [PATCH 061/219] Small change to plantings model, so feature spec passes again --- app/controllers/plantings_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/plantings_controller.rb b/app/controllers/plantings_controller.rb index 0853b897a..b0e339710 100644 --- a/app/controllers/plantings_controller.rb +++ b/app/controllers/plantings_controller.rb @@ -54,7 +54,7 @@ class PlantingsController < ApplicationController def create @planting = Planting.new(planting_params) @planting.owner = current_member - @planting.save! + @planting.save respond_with @planting end @@ -71,7 +71,7 @@ class PlantingsController < ApplicationController private def update_crop_medians - @planting.crop.update_lifespan_medians + @planting.crop.update_lifespan_medians if @planting.crop.present? end def update_planting_medians From 9e5aba75d8afbeb34c7100d3bc19279d58fba732 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 7 Jan 2018 18:35:28 +1300 Subject: [PATCH 062/219] Added full path to jpeg in spec, so does'nt throw asset pipeline errors --- spec/features/crops/crop_photos_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/features/crops/crop_photos_spec.rb b/spec/features/crops/crop_photos_spec.rb index 260283057..f86978645 100644 --- a/spec/features/crops/crop_photos_spec.rb +++ b/spec/features/crops/crop_photos_spec.rb @@ -8,16 +8,16 @@ feature "crop detail page", js: true do let(:harvest) { create :harvest, owner: member } let(:photo1) do - create(:photo, owner: member, title: 'photo 1', fullsize_url: 'photo1.jpg', thumbnail_url: 'thumb1.jpg') + create(:photo, owner: member, title: 'photo 1', fullsize_url: 'http://example.com/photo1.jpg', thumbnail_url: 'http://example.com/thumb1.jpg') end let(:photo2) do - create(:photo, owner: member, title: 'photo 2', fullsize_url: 'photo2.jpg', thumbnail_url: 'thumb2.jpg') + create(:photo, owner: member, title: 'photo 2', fullsize_url: 'http://example.com/photo2.jpg', thumbnail_url: 'http://example.com/thumb2.jpg') end let(:photo3) do - create(:photo, owner: member, title: 'photo 3', fullsize_url: 'photo3.jpg', thumbnail_url: 'thumb3.jpg') + create(:photo, owner: member, title: 'photo 3', fullsize_url: 'http://example.com/photo3.jpg', thumbnail_url: 'http://example.com/thumb3.jpg') end let(:photo4) do - create(:photo, owner: member, title: 'photo 4', fullsize_url: 'photo4.jpg', thumbnail_url: 'thumb4.jpg') + create(:photo, owner: member, title: 'photo 4', fullsize_url: 'http://example.com/photo4.jpg', thumbnail_url: 'http://example.com/thumb4.jpg') end before do From 73ffbb0272dbc31db46c31e475fb99ec329be9c1 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 7 Jan 2018 18:44:03 +1300 Subject: [PATCH 063/219] crop creator is optional doesn't get set when crop has just been requested --- app/models/crop.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/crop.rb b/app/models/crop.rb index 4863b6658..a5105fb94 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -16,7 +16,7 @@ class Crop < ApplicationRecord has_many :seeds has_many :harvests has_many :plant_parts, -> { distinct.reorder("plant_parts.name") }, through: :harvests - belongs_to :creator, class_name: 'Member' + belongs_to :creator, class_name: 'Member', optional: true belongs_to :requester, class_name: 'Member', optional: true belongs_to :parent, class_name: 'Crop', optional: true has_many :varieties, class_name: 'Crop', foreign_key: 'parent_id' From 52e3f03bb52f1b20568a12c91f13810c3ead5888 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 7 Jan 2018 19:25:05 +1300 Subject: [PATCH 064/219] Add timeline to gardens --- app/controllers/gardens_controller.rb | 11 +++++++++++ app/views/gardens/show.html.haml | 9 +++++++++ config/routes.rb | 4 +++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/app/controllers/gardens_controller.rb b/app/controllers/gardens_controller.rb index 35efaf5d2..6a130b19d 100644 --- a/app/controllers/gardens_controller.rb +++ b/app/controllers/gardens_controller.rb @@ -60,6 +60,17 @@ class GardensController < ApplicationController redirect_to(gardens_by_owner_path(owner: @garden.owner)) end + def timeline + @data = [] + @garden = Garden.find(params[:garden_id]) + @garden.plantings.where.not(finished_at: nil) + .where.not(planted_at: nil) + .order(finished_at: :desc).each do |p| + @data << [p.crop.name, p.planted_at, p.finished_at] + end + render json: @data + end + private def garden_params diff --git a/app/views/gardens/show.html.haml b/app/views/gardens/show.html.haml index ef5b41cfb..5ffed7152 100644 --- a/app/views/gardens/show.html.haml +++ b/app/views/gardens/show.html.haml @@ -9,6 +9,11 @@ = tag("meta", property: "og:type", content: "website") = tag("meta", property: "og:url", content: request.original_url) = tag("meta", property: "og:site_name", content: ENV['GROWSTUFF_SITE_NAME']) + +- content_for :scripts do + = javascript_include_tag "charts" + = javascript_include_tag "https://www.gstatic.com/charts/loader.js" + .row .col-md-9 %p.btn-group= render 'gardens/actions', garden: @garden @@ -32,6 +37,10 @@ Why not = link_to 'tell us more.', edit_garden_path(@garden) + %h3 Garden timeline + .row + = timeline garden_timeline_path(@garden), adapter: "google" + %h3 What's planted here? .row - if @current_plantings.size.positive? diff --git a/config/routes.rb b/config/routes.rb index d22049d55..9aa2d3859 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,7 +27,9 @@ Rails.application.routes.draw do get '/plantings/owner/:owner' => 'plantings#index', as: 'plantings_by_owner' get '/plantings/crop/:crop' => 'plantings#index', as: 'plantings_by_crop' - resources :gardens + resources :gardens do + get 'timeline' => 'gardens#timeline' + end get '/gardens/owner/:owner' => 'gardens#index', as: 'gardens_by_owner' resources :seeds From 8e59e68dd76971bbbd723db663d1e162e05535a7 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 7 Jan 2018 19:30:52 +1300 Subject: [PATCH 065/219] Add valid DNS entry to feature spec, so it doesn't fail --- spec/features/crops/crop_photos_spec.rb | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/spec/features/crops/crop_photos_spec.rb b/spec/features/crops/crop_photos_spec.rb index f86978645..9d92449ea 100644 --- a/spec/features/crops/crop_photos_spec.rb +++ b/spec/features/crops/crop_photos_spec.rb @@ -6,18 +6,30 @@ feature "crop detail page", js: true do let(:crop) { create :crop, plantings: [planting], harvests: [harvest] } let(:planting) { create :planting, owner: member } let(:harvest) { create :harvest, owner: member } - + let(:valid_server) { 'https://farm5.staticflickr.com/' } let(:photo1) do - create(:photo, owner: member, title: 'photo 1', fullsize_url: 'http://example.com/photo1.jpg', thumbnail_url: 'http://example.com/thumb1.jpg') + create(:photo, owner: member, + title: 'photo 1', + fullsize_url: "#{valid_server}photo1.jpg", + thumbnail_url: "#{valid_server}thumb1.jpg") end let(:photo2) do - create(:photo, owner: member, title: 'photo 2', fullsize_url: 'http://example.com/photo2.jpg', thumbnail_url: 'http://example.com/thumb2.jpg') + create(:photo, owner: member, + title: 'photo 2', + fullsize_url: "#{valid_server}photo2.jpg", + thumbnail_url: "#{valid_server}thumb2.jpg") end let(:photo3) do - create(:photo, owner: member, title: 'photo 3', fullsize_url: 'http://example.com/photo3.jpg', thumbnail_url: 'http://example.com/thumb3.jpg') + create(:photo, owner: member, + title: 'photo 3', + fullsize_url: "#{valid_server}photo3.jpg", + thumbnail_url: "#{valid_server}thumb3.jpg") end let(:photo4) do - create(:photo, owner: member, title: 'photo 4', fullsize_url: 'http://example.com/photo4.jpg', thumbnail_url: 'http://example.com/thumb4.jpg') + create(:photo, owner: member, + title: 'photo 4', + fullsize_url: "#{valid_server}photo4.jpg", + thumbnail_url: "#{valid_server}thumb4.jpg") end before do From 386dc5ad7f950745a8bb554a2b17cd512649c67a Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 7 Jan 2018 19:37:03 +1300 Subject: [PATCH 066/219] Use factory bot in specs --- spec/features/crops/creating_a_crop_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/crops/creating_a_crop_spec.rb b/spec/features/crops/creating_a_crop_spec.rb index 2b1300485..e03179933 100644 --- a/spec/features/crops/creating_a_crop_spec.rb +++ b/spec/features/crops/creating_a_crop_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' feature "Crop - " do - let!(:crop_wrangler) { create :crop_wrangling_member } - let!(:member) { create :member } + let!(:crop_wrangler) { FactoryBot.create :crop_wrangling_member } + let!(:member) { FactoryBot.create :member } background do login_as member From 865fe106ad2ed2dd0197d560d0a2cebf9765349c Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 7 Jan 2018 22:10:27 +1300 Subject: [PATCH 067/219] Unique member name on notifications spec --- spec/features/notifications_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/notifications_spec.rb b/spec/features/notifications_spec.rb index aa81403d6..8b4442927 100644 --- a/spec/features/notifications_spec.rb +++ b/spec/features/notifications_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' feature "Notifications", :js do let(:sender) { create :member } - let(:recipient) { create :member } + let(:recipient) { create :member, login_name: 'beyonce' } context "On existing notification" do let!(:notification) do From 34280123e5999b8e3c91b45d09cc17b64dec6a98 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 7 Jan 2018 17:25:16 +1300 Subject: [PATCH 068/219] Convert to use chartkick gem Conflicts: Gemfile Gemfile.lock config/initializers/assets.rb --- .overcommit.yml | 1 + Gemfile | 3 +- Gemfile.lock | 5 +- app/assets/javascripts/charts.js | 2 + app/assets/javascripts/crops.js.erb | 37 -- app/assets/javascripts/graphs/bar_group.js | 53 --- .../javascripts/graphs/bar_label_group.js | 45 -- app/assets/javascripts/graphs/height_scale.js | 31 -- .../graphs/horizontal_bar_graph.js | 54 --- app/assets/javascripts/graphs/width_scale.js | 37 -- app/assets/javascripts/highcharts.js | 389 ++++++++++++++++++ app/assets/stylesheets/application.sass | 1 + .../custom_bootstrap/variables.sass | 1 + app/assets/stylesheets/overrides.sass | 19 - app/assets/stylesheets/predictions.sass | 11 + app/controllers/crops_controller.rb | 14 +- app/views/crops/_predictions.html.haml | 55 +-- app/views/crops/show.html.haml | 35 +- app/views/layouts/application.html.haml | 1 + config/initializers/assets.rb | 15 + config/routes.rb | 1 + 21 files changed, 484 insertions(+), 326 deletions(-) create mode 100644 app/assets/javascripts/charts.js delete mode 100644 app/assets/javascripts/graphs/bar_group.js delete mode 100644 app/assets/javascripts/graphs/bar_label_group.js delete mode 100644 app/assets/javascripts/graphs/height_scale.js delete mode 100644 app/assets/javascripts/graphs/horizontal_bar_graph.js delete mode 100644 app/assets/javascripts/graphs/width_scale.js create mode 100644 app/assets/javascripts/highcharts.js create mode 100644 app/assets/stylesheets/predictions.sass create mode 100644 config/initializers/assets.rb diff --git a/.overcommit.yml b/.overcommit.yml index d7899885f..200a90e42 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -61,6 +61,7 @@ PreCommit: enabled: true exclude: - 'app/assets/**' + - 'app/assets/javascripts/highcharts.js' - 'spec/javascripts/support/vendor/**' - '**/bootstrap*' command: ['./node_modules/.bin/eslint'] diff --git a/Gemfile b/Gemfile index c5d5d1fe1..1e26e67fc 100644 --- a/Gemfile +++ b/Gemfile @@ -78,8 +78,7 @@ gem 'omniauth-facebook' gem 'omniauth-flickr', '>= 0.0.15' gem 'omniauth-twitter' -# For charting data -gem 'd3-rails', '~> 3.5' # 4.* produces Error: : could not find an object to spy upon for linear() - see https://travis-ci.org/Growstuff/growstuff/jobs/204461482 +gem "chartkick" # client for Elasticsearch. Elasticsearch is a flexible # and powerful, distributed, real-time search and analytics engine. diff --git a/Gemfile.lock b/Gemfile.lock index 31113ea38..637ee3f44 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -99,6 +99,7 @@ GEM capybara-screenshot (1.0.18) capybara (>= 1.0, < 3) launchy + chartkick (2.2.5) childprocess (0.8.0) ffi (~> 1.0, >= 1.0.11) climate_control (0.2.0) @@ -143,8 +144,6 @@ GEM crass (1.0.3) csv_shaper (1.3.0) activesupport (>= 3.0.0) - d3-rails (3.5.17) - railties (>= 3.1) dalli (2.7.6) database_cleaner (1.6.2) debug_inspector (0.0.3) @@ -569,12 +568,12 @@ DEPENDENCIES capybara capybara-email capybara-screenshot + chartkick codeclimate-test-reporter coffee-rails comfortable_mexican_sofa coveralls csv_shaper - d3-rails (~> 3.5) dalli database_cleaner devise diff --git a/app/assets/javascripts/charts.js b/app/assets/javascripts/charts.js new file mode 100644 index 000000000..1c42da456 --- /dev/null +++ b/app/assets/javascripts/charts.js @@ -0,0 +1,2 @@ +// = require Chart.bundle +// = require chartkick diff --git a/app/assets/javascripts/crops.js.erb b/app/assets/javascripts/crops.js.erb index afa16a009..9a60dd60c 100644 --- a/app/assets/javascripts/crops.js.erb +++ b/app/assets/javascripts/crops.js.erb @@ -1,5 +1,3 @@ -//= require graphs/horizontal_bar_graph - if (document.getElementById("cropmap") !== null) { mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>"; mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>"; @@ -51,41 +49,6 @@ function showCropMap(cropmap) { cropmap.addLayer(markers); } -function plantingStats(crop) { - var sunniness_counts = { 'empty': 0, 'sun': 0, 'semi-shade': 0, 'shade': 0 }; - $.each(crop.plantings, function(i, planting) { - if (planting.sunniness) { - sunniness_counts[planting.sunniness]++; - } else { - sunniness_counts['Empty']++; - } - }); - return [ - {name: 'Empty', value: sunniness_counts['empty']}, - {name: 'Sun', value: sunniness_counts['sun']}, - {name: 'Semi-shade', value: sunniness_counts['semi-shade']}, - {name: 'Shade', value: sunniness_counts['shade']} - ]; -} - -if ($("#sunchart")[0] !== null) { - var HorizontalBarGraph = growstuff.HorizontalBarGraph; - $.getJSON(location.pathname + '.json', function (crop) { - data = { - bars: plantingStats(crop), - bar_color: 'steelblue', - width: {size: 300, scale: 'linear'}, - height: {size: 100, scale: 'ordinal'}, - //left is used to shift the bars over so that there is - //room for the labels - margin: {top: 0, right: 0, bottom: 0, left: 100} - }; - - var graph = new HorizontalBarGraph(data); - graph.render(d3.select($('#sunchart')[0])); - }); -} - $('.btn.toggle.crop-hierarchy').click(function () { $('.toggle.crop-hierarchy').toggleClass('hide'); }); diff --git a/app/assets/javascripts/graphs/bar_group.js b/app/assets/javascripts/graphs/bar_group.js deleted file mode 100644 index e8a90c20a..000000000 --- a/app/assets/javascripts/graphs/bar_group.js +++ /dev/null @@ -1,53 +0,0 @@ -// =require d3 -// = require graphs/width_scale -// = require graphs/height_scale - -/* - * This represents bars for a bar graph. - * Currently these are used for HorizontalBarGraph. - */ -(function() { - 'use strict'; - - - var growstuff = (window.growstuff = window.growstuff || {}); - var WidthScale = growstuff.WidthScale; - var HeightScale = growstuff.HeightScale; - -/** - * data object for bar group - * @param {Object} data Graph configuration - */ -function BarGroup(data) { - this._data = data; -} - BarGroup.prototype.render = function(root) { - var data = this._data; - var bars = this._data.bars; - var widthScale = new WidthScale(data).render(); - var heightScale = new HeightScale(data).render(); - - return root.append('g') - .attr('class', 'bar') - .selectAll('rect') - .data(bars.map(function(bar) { - return bar.value; - })) - .enter() - .append('rect') - .attr('y', function(d, i) { - return heightScale(i); - }) - .attr('height', heightScale.rangeBand()) - .attr('fill', data.bar_color) - .attr('width', function(d) { - return widthScale(d); - }) - .append('title') - .text(function(d) { - return 'This value is ' + d + '.'; - }); - }; - - growstuff.BarGroup = BarGroup; -}()); diff --git a/app/assets/javascripts/graphs/bar_label_group.js b/app/assets/javascripts/graphs/bar_label_group.js deleted file mode 100644 index 2da31f567..000000000 --- a/app/assets/javascripts/graphs/bar_label_group.js +++ /dev/null @@ -1,45 +0,0 @@ -// =require d3 -/** - * This file draws the labels to the left of each bar. - */ -(function() { - 'use strict'; - - var growstuff = (window.growstuff = window.growstuff || {}); - /** - * new bar label object - * @param {Object} data Graph configuration - */ - function BarLabelGroup(data) { - this._data = data; - } - - BarLabelGroup.prototype.render = function(d3) { - var bars = this._data.bars; - // vvcopy pasta from spike vv -- this is a good candidate for refactor - var barHeight = 40; - - return d3.append('g') - .attr('class', 'bar-label') - .selectAll('text') - .data(bars.map(function(bar) { - return bar.name; - })) - .enter() - .append('text') - .attr('x', -80) - .attr('y', function(d, i) { - // shrink the margin between each label to give them an even spread with - // bars - var barLabelSpread = 2/3; - // move them downward to line up with bars - var barLabelTopEdge = 17; - return i * barHeight * (barLabelSpread) + barLabelTopEdge; - }) - .text(function(d) { - return d; - }); - }; - - growstuff.BarLabelGroup = BarLabelGroup; -}()); diff --git a/app/assets/javascripts/graphs/height_scale.js b/app/assets/javascripts/graphs/height_scale.js deleted file mode 100644 index 5189ef800..000000000 --- a/app/assets/javascripts/graphs/height_scale.js +++ /dev/null @@ -1,31 +0,0 @@ -// =require d3 - -/** - * Height Scale is used to map the number of bars to the display size of - * the svg - */ - -(function() { - 'use strict'; - - var growstuff = (window.growstuff = window.growstuff || {}); - - /** - * new height scale object - * @param {Object} data Graph configuration - */ - function HeightScale(data) { - this._data = data; - } - - HeightScale.prototype.render = function() { - var data = this._data; - var scaleType = data.height.scale; - - return d3.scale[scaleType]() - .domain(d3.range(data.bars.length)) - .rangeRoundBands([0, data.height.size], 0.05, 0); - }; - - growstuff.HeightScale = HeightScale; -}()); diff --git a/app/assets/javascripts/graphs/horizontal_bar_graph.js b/app/assets/javascripts/graphs/horizontal_bar_graph.js deleted file mode 100644 index ef2333ef1..000000000 --- a/app/assets/javascripts/graphs/horizontal_bar_graph.js +++ /dev/null @@ -1,54 +0,0 @@ -// = require d3 -// = require graphs/bar_group -// = require graphs/bar_label_group - -/** - * Horizontal Bar Graph represents sum total of the graph including all of the parts: - * Bars - * Bar Labels - * - * The main dimensions of the graph are rendered here. - */ - -(function() { - 'use strict'; - - var growstuff = (window.growstuff = window.growstuff || {}); - var BarGroup = growstuff.BarGroup; - var BarLabelGroup = growstuff.BarLabelGroup; - - /** - * create a new graph object - * @param {Object} data Graph configuration - */ - function HorizontalBarGraph(data) { - this._data = data; - this._d3 = d3; - } - - HorizontalBarGraph.prototype.render = function(root) { - var width = this._data.width; - var height = this._data.height; - - var barLabelGroup = new BarLabelGroup(this._data); - var margin = this._data.margin; - - var barGroup = new BarGroup(this._data); - - var svg = root - .append('svg') - .attr('width', width.size + margin.left + margin.right) - .attr('height', height.size + margin.top + margin.bottom) - .append('g') - .attr('class', 'bar-graph') - .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - - - barGroup.render(svg); - barLabelGroup.render(svg); - - return svg; - }; - - growstuff.HorizontalBarGraph = HorizontalBarGraph; -}()); diff --git a/app/assets/javascripts/graphs/width_scale.js b/app/assets/javascripts/graphs/width_scale.js deleted file mode 100644 index 7f2b9dda2..000000000 --- a/app/assets/javascripts/graphs/width_scale.js +++ /dev/null @@ -1,37 +0,0 @@ -// =require d3 - -/** - * Width scale is used to map the value for the length of each bar - * to the display size of the svg - */ -(function() { - 'use strict'; - - var growstuff = (window.growstuff = window.growstuff || {}); - - /** - * Object for WidthScale - * @param {Object} data Graph configuration - */ - function WidthScale(data) { - this._data = data; - } - - WidthScale.prototype.render = function() { - var data = this._data; - var scaleType = data.width.scale; - var axisSize = data.width.size; - - return d3.scale[scaleType]() - .domain([0, this.getMaxValue()]) - .range([0, axisSize]); - }; - - WidthScale.prototype.getMaxValue = function() { - return d3.max(this._data.bars.map(function(bar) { - return bar.value; - })); - }; - - growstuff.WidthScale = WidthScale; -}()); diff --git a/app/assets/javascripts/highcharts.js b/app/assets/javascripts/highcharts.js new file mode 100644 index 000000000..9554ef58b --- /dev/null +++ b/app/assets/javascripts/highcharts.js @@ -0,0 +1,389 @@ +/* + Highcharts JS v6.0.4 (2017-12-15) + + (c) 2009-2016 Torstein Honsi + + License: www.highcharts.com/license +*/ +(function(S,M){"object"===typeof module&&module.exports?module.exports=S.document?M(S):M:S.Highcharts=M(S)})("undefined"!==typeof window?window:this,function(S){var M=function(){var a="undefined"===typeof S?window:S,E=a.document,D=a.navigator&&a.navigator.userAgent||"",H=E&&E.createElementNS&&!!E.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect,p=/(edge|msie|trident)/i.test(D)&&!a.opera,f=/Firefox/.test(D),l=f&&4>parseInt(D.split("Firefox/")[1],10);return a.Highcharts?a.Highcharts.error(16, +!0):{product:"Highcharts",version:"6.0.4",deg2rad:2*Math.PI/360,doc:E,hasBidiBug:l,hasTouch:E&&void 0!==E.documentElement.ontouchstart,isMS:p,isWebKit:/AppleWebKit/.test(D),isFirefox:f,isTouchDevice:/(Mobile|Android|Windows Phone)/.test(D),SVG_NS:"http://www.w3.org/2000/svg",chartCount:0,seriesTypes:{},symbolSizes:{},svg:H,win:a,marginNames:["plotTop","marginRight","marginBottom","plotLeft"],noop:function(){},charts:[]}}();(function(a){a.timers=[];var E=a.charts,D=a.doc,H=a.win;a.error=function(p, +f){p=a.isNumber(p)?"Highcharts error #"+p+": www.highcharts.com/errors/"+p:p;if(f)throw Error(p);H.console&&console.log(p)};a.Fx=function(a,f,l){this.options=f;this.elem=a;this.prop=l};a.Fx.prototype={dSetter:function(){var a=this.paths[0],f=this.paths[1],l=[],r=this.now,n=a.length,w;if(1===r)l=this.toD;else if(n===f.length&&1>r)for(;n--;)w=parseFloat(a[n]),l[n]=isNaN(w)?f[n]:r*parseFloat(f[n]-w)+w;else l=f;this.elem.attr("d",l,null,!0)},update:function(){var a=this.elem,f=this.prop,l=this.now,r= +this.options.step;if(this[f+"Setter"])this[f+"Setter"]();else a.attr?a.element&&a.attr(f,l,null,!0):a.style[f]=l+this.unit;r&&r.call(a,l,this)},run:function(p,f,l){var r=this,n=r.options,w=function(a){return w.stopped?!1:r.step(a)},u=H.requestAnimationFrame||function(a){setTimeout(a,13)},e=function(){for(var h=0;h=u+this.startTime?(this.now=this.end,this.pos=1,this.update(),l=e[this.prop]=!0,a.objectEach(e,function(a){!0!==a&&(l=!1)}),l&&w&&w.call(n),p=!1):(this.pos=r.easing((f-this.startTime)/u),this.now=this.start+(this.end- +this.start)*this.pos,this.update(),p=!0);return p},initPath:function(p,f,l){function r(a){var b,c;for(k=a.length;k--;)b="M"===a[k]||"L"===a[k],c=/[a-zA-Z]/.test(a[k+3]),b&&c&&a.splice(k+1,0,a[k+1],a[k+2],a[k+1],a[k+2])}function n(a,b){for(;a.lengtha&&-Infinityw?"AM":"PM",P:12>w?"am":"pm",S:k(n.getSeconds()),L:k(Math.round(f%1E3),3)},a.dateFormats);a.objectEach(r,function(a,b){for(;-1!==p.indexOf("%"+b);)p=p.replace("%"+b,"function"===typeof a?a(f):a)});return l?p.substr(0,1).toUpperCase()+p.substr(1):p};a.formatSingle=function(p,f){var l=/\.([0-9])/,r=a.defaultOptions.lang;/f$/.test(p)?(l=(l=p.match(l))?l[1]:-1,null!==f&&(f=a.numberFormat(f,l,r.decimalPoint,-1=l&&(f=[1/l])));for(r=0;r=p||!n&&w<=(f[r]+(f[r+1]||f[r]))/2);r++);return u=a.correctFloat(u*l,-Math.round(Math.log(.001)/Math.LN10))};a.stableSort=function(a,f){var l=a.length,p,n;for(n=0;nl&&(l=a[f]);return l};a.destroyObjectProperties=function(p,f){a.objectEach(p,function(a,r){a&&a!==f&&a.destroy&&a.destroy();delete p[r]})};a.discardElement=function(p){var f=a.garbageBin;f||(f=a.createElement("div"));p&&f.appendChild(p);f.innerHTML=""};a.correctFloat=function(a,f){return parseFloat(a.toPrecision(f||14))};a.setAnimation=function(p,f){f.renderer.globalAnimation=a.pick(p,f.options.chart.animation, +!0)};a.animObject=function(p){return a.isObject(p)?a.merge(p):{duration:p?500:0}};a.timeUnits={millisecond:1,second:1E3,minute:6E4,hour:36E5,day:864E5,week:6048E5,month:24192E5,year:314496E5};a.numberFormat=function(p,f,l,r){p=+p||0;f=+f;var n=a.defaultOptions.lang,w=(p.toString().split(".")[1]||"").split("e")[0].length,u,e,h=p.toString().split("e");-1===f?f=Math.min(w,20):a.isNumber(f)?f&&h[1]&&0>h[1]&&(u=f+ +h[1],0<=u?(h[0]=(+h[0]).toExponential(u).split("e")[0],f=u):(h[0]=h[0].split(".")[0]||0, +p=20>f?(h[0]*Math.pow(10,h[1])).toFixed(f):0,h[1]=0)):f=2;e=(Math.abs(h[1]?h[0]:p)+Math.pow(10,-Math.max(f,w)-1)).toFixed(f);w=String(a.pInt(e));u=3p?"-":"")+(u?w.substr(0,u)+r:"");p+=w.substr(u).replace(/(\d{3})(?=\d)/g,"$1"+r);f&&(p+=l+e.slice(-f));h[1]&&0!==+p&&(p+="e"+h[1]);return p};Math.easeInOutSine=function(a){return-.5*(Math.cos(Math.PI*a)-1)};a.getStyle=function(p,f,l){if("width"===f)return Math.min(p.offsetWidth, +p.scrollWidth)-a.getStyle(p,"padding-left")-a.getStyle(p,"padding-right");if("height"===f)return Math.min(p.offsetHeight,p.scrollHeight)-a.getStyle(p,"padding-top")-a.getStyle(p,"padding-bottom");H.getComputedStyle||a.error(27,!0);if(p=H.getComputedStyle(p,void 0))p=p.getPropertyValue(f),a.pick(l,"opacity"!==f)&&(p=a.pInt(p));return p};a.inArray=function(p,f){return(a.indexOfPolyfill||Array.prototype.indexOf).call(f,p)};a.grep=function(p,f){return(a.filterPolyfill||Array.prototype.filter).call(p, +f)};a.find=Array.prototype.find?function(a,f){return a.find(f)}:function(a,f){var l,r=a.length;for(l=0;l>16,(l&65280)>> +8,l&255,1]:4===f&&(n=[(l&3840)>>4|(l&3840)>>8,(l&240)>>4|l&240,(l&15)<<4|l&15,1])),!n)for(w=this.parsers.length;w--&&!n;)u=this.parsers[w],(f=u.regex.exec(l))&&(n=u.parse(f));this.rgba=n||[]},get:function(a){var f=this.input,n=this.rgba,l;this.stops?(l=p(f),l.stops=[].concat(l.stops),E(this.stops,function(n,e){l.stops[e]=[l.stops[e][0],n.get(a)]})):l=n&&D(n[0])?"rgb"===a||!a&&1===n[3]?"rgb("+n[0]+","+n[1]+","+n[2]+")":"a"===a?n[3]:"rgba("+n.join(",")+")":f;return l},brighten:function(a){var l,n=this.rgba; +if(this.stops)E(this.stops,function(n){n.brighten(a)});else if(D(a)&&0!==a)for(l=0;3>l;l++)n[l]+=f(255*a),0>n[l]&&(n[l]=0),255y.width)y={width:0,height:0}}else y=this.htmlGetBBox();b.isSVG&& +(a=y.width,b=y.height,q&&"11px"===q.fontSize&&17===Math.round(b)&&(y.height=b=14),g&&(y.width=Math.abs(b*Math.sin(v))+Math.abs(a*Math.cos(v)),y.height=Math.abs(b*Math.cos(v))+Math.abs(a*Math.sin(v))));if(A&&0]*>/g,"")))},textSetter:function(a){a!==this.textStr&&(delete this.bBox,this.textStr=a,this.added&&this.renderer.buildText(this))},fillSetter:function(a,g,b){"string"===typeof a?b.setAttribute(g,a):a&&this.colorGradient(a,g,b)},visibilitySetter:function(a,g,b){"inherit"===a?b.removeAttribute(g):this[g]!==a&&b.setAttribute(g,a);this[g]=a},zIndexSetter:function(a,b){var v=this.renderer,y=this.parentGroup,c=(y||v).element||v.box,k,d=this.element,q,e,v=c===v.box;k=this.added;var z;u(a)&& +(d.zIndex=a,a=+a,this[b]===a&&(k=!1),this[b]=a);if(k){(a=this.zIndex)&&y&&(y.handleZ=!0);b=c.childNodes;for(z=b.length-1;0<=z&&!q;z--)if(y=b[z],k=y.zIndex,e=!u(k),y!==d)if(0>a&&e&&!v&&!z)c.insertBefore(d,b[z]),q=!0;else if(g(k)<=a||e&&(!u(a)||0<=a))c.insertBefore(d,b[z+1]||null),q=!0;q||(c.insertBefore(d,b[v?3:0]||null),q=!0)}return q},_defaultSetter:function(a,g,b){b.setAttribute(g,a)}});E.prototype.yGetter=E.prototype.xGetter;E.prototype.translateXSetter=E.prototype.translateYSetter=E.prototype.rotationSetter= +E.prototype.verticalAlignSetter=E.prototype.rotationOriginXSetter=E.prototype.rotationOriginYSetter=E.prototype.scaleXSetter=E.prototype.scaleYSetter=E.prototype.matrixSetter=function(a,g){this[g]=a;this.doTransform=!0};E.prototype["stroke-widthSetter"]=E.prototype.strokeSetter=function(a,g,b){this[g]=a;this.stroke&&this["stroke-width"]?(E.prototype.fillSetter.call(this,this.stroke,"stroke",b),b.setAttribute("stroke-width",this["stroke-width"]),this.hasStroke=!0):"stroke-width"===g&&0===a&&this.hasStroke&& +(b.removeAttribute("stroke"),this.hasStroke=!1)};D=a.SVGRenderer=function(){this.init.apply(this,arguments)};c(D.prototype,{Element:E,SVG_NS:P,init:function(a,g,b,v,c,k){var y;v=this.createElement("svg").attr({version:"1.1","class":"highcharts-root"}).css(this.getStyle(v));y=v.element;a.appendChild(y);f(a,"dir","ltr");-1===a.innerHTML.indexOf("xmlns")&&f(y,"xmlns",this.SVG_NS);this.isSVG=!0;this.box=y;this.boxWrapper=v;this.alignedObjects=[];this.url=(x||N)&&m.getElementsByTagName("base").length? +R.location.href.replace(/#.*?$/,"").replace(/<[^>]*>/g,"").replace(/([\('\)])/g,"\\$1").replace(/ /g,"%20"):"";this.createElement("desc").add().element.appendChild(m.createTextNode("Created with Highcharts 6.0.4"));this.defs=this.createElement("defs").add();this.allowHTML=k;this.forExport=c;this.gradients={};this.cache={};this.cacheKeys=[];this.imgCount=0;this.setSize(g,b,!1);var d;x&&a.getBoundingClientRect&&(g=function(){n(a,{left:0,top:0});d=a.getBoundingClientRect();n(a,{left:Math.ceil(d.left)- +d.left+"px",top:Math.ceil(d.top)-d.top+"px"})},g(),this.unSubPixelFix=H(R,"resize",g))},getStyle:function(a){return this.style=c({fontFamily:'"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif',fontSize:"12px"},a)},setStyle:function(a){this.boxWrapper.css(this.getStyle(a))},isHidden:function(){return!this.boxWrapper.getBBox().width},destroy:function(){var a=this.defs;this.box=null;this.boxWrapper=this.boxWrapper.destroy();h(this.gradients||{});this.gradients=null;a&&(this.defs=a.destroy()); +this.unSubPixelFix&&this.unSubPixelFix();return this.alignedObjects=null},createElement:function(a){var g=new this.Element;g.init(this,a);return g},draw:A,getRadialAttr:function(a,g){return{cx:a[0]-a[2]/2+g.cx*a[2],cy:a[1]-a[2]/2+g.cy*a[2],r:g.r*a[2]}},getSpanWidth:function(a,g){var b=a.getBBox(!0).width;!L&&this.forExport&&(b=this.measureSpanWidth(g.firstChild.data,a.styles));return b},applyEllipsis:function(a,g,b,v){var c=a.rotation,y=b,k,d=0,q=b.length,e=function(a){g.removeChild(g.firstChild); +a&&g.appendChild(m.createTextNode(a))},z;a.rotation=0;y=this.getSpanWidth(a,g);if(z=y>v){for(;d<=q;)k=Math.ceil((d+q)/2),y=b.substring(0,k)+"\u2026",e(y),y=this.getSpanWidth(a,g),d===q?d=q+1:y>v?q=k-1:d=k;0===q&&e("")}a.rotation=c;return z},escapes:{"\x26":"\x26amp;","\x3c":"\x26lt;","\x3e":"\x26gt;","'":"\x26#39;",'"':"\x26quot;"},buildText:function(a){var b=a.element,v=this,c=v.forExport,y=G(a.textStr,"").toString(),q=-1!==y.indexOf("\x3c"),e=b.childNodes,z,h,A,J,t=f(b,"x"),x=a.styles,B=a.textWidth, +l=x&&x.lineHeight,C=x&&x.textOutline,u=x&&"ellipsis"===x.textOverflow,Q=x&&"nowrap"===x.whiteSpace,w=x&&x.fontSize,R,I,r=e.length,x=B&&!a.added&&this.box,p=function(a){var c;c=/(px|em)$/.test(a&&a.style.fontSize)?a.style.fontSize:w||v.style.fontSize||12;return l?g(l):v.fontMetrics(c,a.getAttribute("style")?a:b).h},K=function(a){F(v.escapes,function(g,b){a=a.replace(new RegExp(g,"g"),b)});return a};R=[y,u,Q,l,C,w,B].join();if(R!==a.textCache){for(a.textCache=R;r--;)b.removeChild(e[r]);q||C||u||B|| +-1!==y.indexOf(" ")?(z=/<.*class="([^"]+)".*>/,h=/<.*style="([^"]+)".*>/,A=/<.*href="([^"]+)".*>/,x&&x.appendChild(b),y=q?y.replace(/<(b|strong)>/g,'\x3cspan style\x3d"font-weight:bold"\x3e').replace(/<(i|em)>/g,'\x3cspan style\x3d"font-style:italic"\x3e').replace(//g,"\x3c/span\x3e").split(//g):[y],y=k(y,function(a){return""!==a}),d(y,function(g,y){var k,q=0;g=g.replace(/^\s+|\s+$/g,"").replace(//g,"\x3c/span\x3e|||"); +k=g.split("|||");d(k,function(g){if(""!==g||1===k.length){var d={},e=m.createElementNS(v.SVG_NS,"tspan"),x,F;z.test(g)&&(x=g.match(z)[1],f(e,"class",x));h.test(g)&&(F=g.match(h)[1].replace(/(;| |^)color([ :])/,"$1fill$2"),f(e,"style",F));A.test(g)&&!c&&(f(e,"onclick",'location.href\x3d"'+g.match(A)[1]+'"'),f(e,"class","highcharts-anchor"),n(e,{cursor:"pointer"}));g=K(g.replace(/<[a-zA-Z\/](.|\n)*?>/g,"")||" ");if(" "!==g){e.appendChild(m.createTextNode(g));q?d.dx=0:y&&null!==t&&(d.x=t);f(e,d);b.appendChild(e); +!q&&I&&(!L&&c&&n(e,{display:"block"}),f(e,"dy",p(e)));if(B){d=g.replace(/([^\^])-/g,"$1- ").split(" ");x=1B,void 0===J&&(J=g),g&&1!==d.length?(e.removeChild(e.firstChild),O.unshift(d.pop())):(d=O,O=[],d.length&&!Q&&(e=m.createElementNS(P,"tspan"),f(e,{dy:C,x:t}),F&&f(e,"style",F),b.appendChild(e)),l>B&&(B=l)),d.length&&e.appendChild(m.createTextNode(d.join(" ").replace(/- /g, +"-")));a.rotation=G}q++}}});I=I||b.childNodes.length}),J&&a.attr("title",a.textStr),x&&x.removeChild(b),C&&a.applyTextOutline&&a.applyTextOutline(C)):b.appendChild(m.createTextNode(K(y)))}},getContrast:function(a){a=r(a).rgba;return 510Math.abs(c.end-c.start-2*Math.PI));var y=Math.cos(k),z=Math.sin(k),h=Math.cos(e),e=Math.sin(e);c=.001>c.end-k-Math.PI?0:1;d=["M",a+d*y,g+q*z,"A",d,q,0,c,1,a+d*h,g+q*e];u(b)&&d.push(v?"M":"L",a+b* +h,g+b*e,"A",b,b,0,c,0,a+b*y,g+b*z);d.push(v?"":"Z");return d},callout:function(a,g,b,v,c){var d=Math.min(c&&c.r||0,b,v),k=d+6,q=c&&c.anchorX;c=c&&c.anchorY;var e;e=["M",a+d,g,"L",a+b-d,g,"C",a+b,g,a+b,g,a+b,g+d,"L",a+b,g+v-d,"C",a+b,g+v,a+b,g+v,a+b-d,g+v,"L",a+d,g+v,"C",a,g+v,a,g+v,a,g+v-d,"L",a,g+d,"C",a,g,a,g,a+d,g];q&&q>b?c>g+k&&cq?c>g+k&&cv&&q>a+k&&qc&&q>a+k&&qa?a+3:Math.round(1.2*a);return{h:b,b:Math.round(.8* +b),f:a}},rotCorr:function(a,g,b){var v=a;g&&b&&(v=Math.max(v*Math.cos(g*e),4));return{x:-a/3*Math.sin(g*e),y:v}},label:function(g,b,k,e,z,h,m,A,L){var y=this,J=y.g("button"!==L&&"label"),t=J.text=y.text("",0,0,m).attr({zIndex:1}),x,F,n=0,B=3,l=0,C,f,Q,G,w,R={},I,P,r=/^url\((.*?)\)$/.test(e),p=r,K,O,N,T;L&&J.addClass("highcharts-"+L);p=r;K=function(){return(I||0)%2/2};O=function(){var a=t.element.style,g={};F=(void 0===C||void 0===f||w)&&u(t.textStr)&&t.getBBox();J.width=(C||F.width||0)+2*B+l;J.height= +(f||F.height||0)+2*B;P=B+y.fontMetrics(a&&a.fontSize,t).b;p&&(x||(J.box=x=y.symbols[e]||r?y.symbol(e):y.rect(),x.addClass(("button"===L?"":"highcharts-label-box")+(L?" highcharts-"+L+"-box":"")),x.add(J),a=K(),g.x=a,g.y=(A?-P:0)+a),g.width=Math.round(J.width),g.height=Math.round(J.height),x.attr(c(g,R)),R={})};N=function(){var a=l+B,g;g=A?0:P;u(C)&&F&&("center"===w||"right"===w)&&(a+={center:.5,right:1}[w]*(C-F.width));if(a!==t.x||g!==t.y)t.attr("x",a),void 0!==g&&t.attr("y",g);t.x=a;t.y=g};T=function(a, +g){x?x.attr(a,g):R[a]=g};J.onAdd=function(){t.add(J);J.attr({text:g||0===g?g:"",x:b,y:k});x&&u(z)&&J.attr({anchorX:z,anchorY:h})};J.widthSetter=function(g){C=a.isNumber(g)?g:null};J.heightSetter=function(a){f=a};J["text-alignSetter"]=function(a){w=a};J.paddingSetter=function(a){u(a)&&a!==B&&(B=J.padding=a,N())};J.paddingLeftSetter=function(a){u(a)&&a!==l&&(l=a,N())};J.alignSetter=function(a){a={left:0,center:.5,right:1}[a];a!==n&&(n=a,F&&J.attr({x:Q}))};J.textSetter=function(a){void 0!==a&&t.textSetter(a); +O();N()};J["stroke-widthSetter"]=function(a,g){a&&(p=!0);I=this["stroke-width"]=a;T(g,a)};J.strokeSetter=J.fillSetter=J.rSetter=function(a,g){"r"!==g&&("fill"===g&&a&&(p=!0),J[g]=a);T(g,a)};J.anchorXSetter=function(a,g){z=J.anchorX=a;T(g,Math.round(a)-K()-Q)};J.anchorYSetter=function(a,g){h=J.anchorY=a;T(g,a-G)};J.xSetter=function(a){J.x=a;n&&(a-=n*((C||F.width)+2*B));Q=Math.round(a);J.attr("translateX",Q)};J.ySetter=function(a){G=J.y=Math.round(a);J.attr("translateY",G)};var U=J.css;return c(J,{css:function(a){if(a){var g= +{};a=q(a);d(J.textProps,function(b){void 0!==a[b]&&(g[b]=a[b],delete a[b])});t.css(g)}return U.call(J,a)},getBBox:function(){return{width:F.width+2*B,height:F.height+2*B,x:F.x-B,y:F.y-B}},shadow:function(a){a&&(O(),x&&x.shadow(a));return J},destroy:function(){v(J.element,"mouseenter");v(J.element,"mouseleave");t&&(t=t.destroy());x&&(x=x.destroy());E.prototype.destroy.call(J);J=y=O=N=T=null}})}});a.Renderer=D})(M);(function(a){var E=a.attr,D=a.createElement,H=a.css,p=a.defined,f=a.each,l=a.extend, +r=a.isFirefox,n=a.isMS,w=a.isWebKit,u=a.pick,e=a.pInt,h=a.SVGRenderer,m=a.win,d=a.wrap;l(a.SVGElement.prototype,{htmlCss:function(a){var b=this.element;if(b=a&&"SPAN"===b.tagName&&a.width)delete a.width,this.textWidth=b,this.updateTransform();a&&"ellipsis"===a.textOverflow&&(a.whiteSpace="nowrap",a.overflow="hidden");this.styles=l(this.styles,a);H(this.element,a);return this},htmlGetBBox:function(){var a=this.element;return{x:a.offsetLeft,y:a.offsetTop,width:a.offsetWidth,height:a.offsetHeight}}, +htmlUpdateTransform:function(){if(this.added){var a=this.renderer,b=this.element,d=this.translateX||0,z=this.translateY||0,h=this.x||0,m=this.y||0,x=this.textAlign||"left",n={left:0,center:.5,right:1}[x],t=this.styles;H(b,{marginLeft:d,marginTop:z});this.shadows&&f(this.shadows,function(a){H(a,{marginLeft:d+1,marginTop:z+1})});this.inverted&&f(b.childNodes,function(c){a.invertChild(c,b)});if("SPAN"===b.tagName){var l=this.rotation,u=e(this.textWidth),q=t&&t.whiteSpace,A=[l,x,b.innerHTML,this.textWidth, +this.textAlign].join();A!==this.cTT&&(t=a.fontMetrics(b.style.fontSize).b,p(l)&&this.setSpanRotation(l,n,t),H(b,{width:"",whiteSpace:q||"nowrap"}),b.offsetWidth>u&&/[ \-]/.test(b.textContent||b.innerText)&&H(b,{width:u+"px",display:"block",whiteSpace:q||"normal"}),this.getSpanCorrection(b.offsetWidth,t,n,l,x));H(b,{left:h+(this.xCorr||0)+"px",top:m+(this.yCorr||0)+"px"});w&&(t=b.offsetHeight);this.cTT=A}}else this.alignOnAdd=!0},setSpanRotation:function(a,b,d){var c={},k=this.renderer.getTransformKey(); +c[k]=c.transform="rotate("+a+"deg)";c[k+(r?"Origin":"-origin")]=c.transformOrigin=100*b+"% "+d+"px";H(this.element,c)},getSpanCorrection:function(a,b,d){this.xCorr=-a*d;this.yCorr=-b}});l(h.prototype,{getTransformKey:function(){return n&&!/Edge/.test(m.navigator.userAgent)?"-ms-transform":w?"-webkit-transform":r?"MozTransform":m.opera?"-o-transform":""},html:function(a,b,k){var c=this.createElement("span"),e=c.element,h=c.renderer,m=h.isSVG,w=function(a,b){f(["opacity","visibility"],function(c){d(a, +c+"Setter",function(a,c,d,k){a.call(this,c,d,k);b[d]=c})})};c.textSetter=function(a){a!==e.innerHTML&&delete this.bBox;this.textStr=a;e.innerHTML=u(a,"");c.htmlUpdateTransform()};m&&w(c,c.element.style);c.xSetter=c.ySetter=c.alignSetter=c.rotationSetter=function(a,b){"align"===b&&(b="textAlign");c[b]=a;c.htmlUpdateTransform()};c.attr({text:a,x:Math.round(b),y:Math.round(k)}).css({fontFamily:this.style.fontFamily,fontSize:this.style.fontSize,position:"absolute"});e.style.whiteSpace="nowrap";c.css= +c.htmlCss;m&&(c.add=function(a){var b,d=h.box.parentNode,k=[];if(this.parentGroup=a){if(b=a.div,!b){for(;a;)k.push(a),a=a.parentGroup;f(k.reverse(),function(a){function e(g,b){a[b]=g;n?q[h.getTransformKey()]="translate("+(a.x||a.translateX)+"px,"+(a.y||a.translateY)+"px)":"translateX"===b?q.left=g+"px":q.top=g+"px";a.doTransform=!0}var q,g=E(a.element,"class");g&&(g={className:g});b=a.div=a.div||D("div",g,{position:"absolute",left:(a.translateX||0)+"px",top:(a.translateY||0)+"px",display:a.display, +opacity:a.opacity,pointerEvents:a.styles&&a.styles.pointerEvents},b||d);q=b.style;l(a,{classSetter:function(a){return function(g){this.element.setAttribute("class",g);a.className=g}}(b),on:function(){k[0].div&&c.on.apply({element:k[0].div},arguments);return a},translateXSetter:e,translateYSetter:e});w(a,q)})}}else b=d;b.appendChild(e);c.added=!0;c.alignOnAdd&&c.htmlUpdateTransform();return c});return c}})})(M);(function(a){function E(){var n=a.defaultOptions.global,l=r.moment;if(n.timezone){if(l)return function(a){return-l.tz(a, +n.timezone).utcOffset()};a.error(25)}return n.useUTC&&n.getTimezoneOffset}function D(){var n=a.defaultOptions.global,f,u=n.useUTC,e=u?"getUTC":"get",h=u?"setUTC":"set",m="Minutes Hours Day Date Month FullYear".split(" "),d=m.concat(["Milliseconds","Seconds"]);a.Date=f=n.Date||r.Date;f.hcTimezoneOffset=u&&n.timezoneOffset;f.hcGetTimezoneOffset=E();f.hcHasTimeZone=!(!f.hcTimezoneOffset&&!f.hcGetTimezoneOffset);f.hcMakeTime=function(a,b,d,e,h,m){var c;u?(c=f.UTC.apply(0,arguments),c+=p(c)):c=(new f(a, +b,l(d,1),l(e,0),l(h,0),l(m,0))).getTime();return c};for(n=0;nb&&e-k*zm&&(p=Math.round((h-e)/Math.cos(b*r)));else if(h=e+(1-k)*z,e-k*zm&&(I=m-a.x+I*k,x=-1),I=Math.min(B,I),II||f.autoRotation&&(c.styles||{}).width)p=I;p&&(t.width=p,(n.style||{}).textOverflow||(t.textOverflow="ellipsis"),c.css(t))},getPosition:function(a,f,l,e){var h=this.axis,m=h.chart,d=e&&m.oldChartHeight||m.chartHeight;return{x:a?h.translate(f+l,null,null,e)+h.transB:h.left+h.offset+(h.opposite?(e&&m.oldChartWidth||m.chartWidth)-h.right-h.left:0),y:a?d-h.bottom+h.offset-(h.opposite?h.height:0):d-h.translate(f+l,null,null,e)-h.transB}},getLabelPosition:function(a, +f,l,e,h,m,d,c){var b=this.axis,k=b.transA,z=b.reversed,B=b.staggerLines,n=b.tickRotCorr||{x:0,y:0},x=h.y,u=e||b.reserveSpaceDefault?0:-b.labelOffset*("center"===b.labelAlign?.5:1);D(x)||(x=0===b.side?l.rotation?-8:-l.getBBox().height:2===b.side?n.y+8:Math.cos(l.rotation*r)*(n.y-l.getBBox(!1,0).height/2));a=a+h.x+u+n.x-(m&&e?m*k*(z?-1:1):0);f=f+x-(m&&!e?m*k*(z?1:-1):0);B&&(l=d/(c||1)%B,b.opposite&&(l=B-l-1),f+=b.labelOffset/B*l);return{x:a,y:Math.round(f)}},getMarkPath:function(a,f,l,e,h,m){return m.crispLine(["M", +a,f,"L",a+(h?0:-l),f+(h?l:0)],e)},renderGridLine:function(a,f,l){var e=this.axis,h=e.options,m=this.gridLine,d={},c=this.pos,b=this.type,k=e.tickmarkOffset,z=e.chart.renderer,B=b?b+"Grid":"grid",n=h[B+"LineWidth"],x=h[B+"LineColor"],h=h[B+"LineDashStyle"];m||(d.stroke=x,d["stroke-width"]=n,h&&(d.dashstyle=h),b||(d.zIndex=1),a&&(d.opacity=0),this.gridLine=m=z.path().attr(d).addClass("highcharts-"+(b?b+"-":"")+"grid-line").add(e.gridGroup));if(!a&&m&&(a=e.getPlotLinePath(c+k,m.strokeWidth()*l,a,!0)))m[this.isNew? +"attr":"animate"]({d:a,opacity:f})},renderMark:function(a,f,u){var e=this.axis,h=e.options,m=e.chart.renderer,d=this.type,c=d?d+"Tick":"tick",b=e.tickSize(c),k=this.mark,z=!k,B=a.x;a=a.y;var n=l(h[c+"Width"],!d&&e.isXAxis?1:0),h=h[c+"Color"];b&&(e.opposite&&(b[0]=-b[0]),z&&(this.mark=k=m.path().addClass("highcharts-"+(d?d+"-":"")+"tick").add(e.axisGroup),k.attr({stroke:h,"stroke-width":n})),k[z?"attr":"animate"]({d:this.getMarkPath(B,a,b[0],k.strokeWidth()*u,e.horiz,m),opacity:f}))},renderLabel:function(a, +f,u,e){var h=this.axis,m=h.horiz,d=h.options,c=this.label,b=d.labels,k=b.step,h=h.tickmarkOffset,z=!0,B=a.x;a=a.y;c&&p(B)&&(c.xy=a=this.getLabelPosition(B,a,c,m,b,h,e,k),this.isFirst&&!this.isLast&&!l(d.showFirstLabel,1)||this.isLast&&!this.isFirst&&!l(d.showLastLabel,1)?z=!1:!m||b.step||b.rotation||f||0===u||this.handleOverflow(a),k&&e%k&&(z=!1),z&&p(a.y)?(a.opacity=u,c[this.isNewLabel?"attr":"animate"](a),this.isNewLabel=!1):(c.attr("y",-9999),this.isNewLabel=!0))},render:function(a,f,u){var e= +this.axis,h=e.horiz,m=this.getPosition(h,this.pos,e.tickmarkOffset,f),d=m.x,c=m.y,e=h&&d===e.pos+e.len||!h&&c===e.pos?-1:1;u=l(u,1);this.isActive=!0;this.renderGridLine(f,u,e);this.renderMark(m,u,e);this.renderLabel(m,f,u,a);this.isNew=!1},destroy:function(){H(this,this.axis)}}})(M);var V=function(a){var E=a.addEvent,D=a.animObject,H=a.arrayMax,p=a.arrayMin,f=a.color,l=a.correctFloat,r=a.defaultOptions,n=a.defined,w=a.deg2rad,u=a.destroyObjectProperties,e=a.each,h=a.extend,m=a.fireEvent,d=a.format, +c=a.getMagnitude,b=a.grep,k=a.inArray,z=a.isArray,B=a.isNumber,I=a.isString,x=a.merge,K=a.normalizeTickInterval,t=a.objectEach,C=a.pick,N=a.removeEvent,q=a.splat,A=a.syncTimeout,F=a.Tick,G=function(){this.init.apply(this,arguments)};a.extend(G.prototype,{defaultOptions:{dateTimeLabelFormats:{millisecond:"%H:%M:%S.%L",second:"%H:%M:%S",minute:"%H:%M",hour:"%H:%M",day:"%e. %b",week:"%e. %b",month:"%b '%y",year:"%Y"},endOnTick:!1,labels:{enabled:!0,style:{color:"#666666",cursor:"default",fontSize:"11px"}, +x:0},maxPadding:.01,minorTickLength:2,minorTickPosition:"outside",minPadding:.01,startOfWeek:1,startOnTick:!1,tickLength:10,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",title:{align:"middle",style:{color:"#666666"}},type:"linear",minorGridLineColor:"#f2f2f2",minorGridLineWidth:1,minorTickColor:"#999999",lineColor:"#ccd6eb",lineWidth:1,gridLineColor:"#e6e6e6",tickColor:"#ccd6eb"},defaultYAxisOptions:{endOnTick:!0,tickPixelInterval:72,showLastLabel:!0,labels:{x:-8},maxPadding:.05, +minPadding:.05,startOnTick:!0,title:{rotation:270,text:"Values"},stackLabels:{allowOverlap:!1,enabled:!1,formatter:function(){return a.numberFormat(this.total,-1)},style:{fontSize:"11px",fontWeight:"bold",color:"#000000",textOutline:"1px contrast"}},gridLineWidth:1,lineWidth:0},defaultLeftAxisOptions:{labels:{x:-15},title:{rotation:270}},defaultRightAxisOptions:{labels:{x:15},title:{rotation:90}},defaultBottomAxisOptions:{labels:{autoRotation:[-45],x:0},title:{rotation:0}},defaultTopAxisOptions:{labels:{autoRotation:[-45], +x:0},title:{rotation:0}},init:function(a,b){var g=b.isX,v=this;v.chart=a;v.horiz=a.inverted&&!v.isZAxis?!g:g;v.isXAxis=g;v.coll=v.coll||(g?"xAxis":"yAxis");v.opposite=b.opposite;v.side=b.side||(v.horiz?v.opposite?0:2:v.opposite?1:3);v.setOptions(b);var c=this.options,d=c.type;v.labelFormatter=c.labels.formatter||v.defaultLabelFormatter;v.userOptions=b;v.minPixelPadding=0;v.reversed=c.reversed;v.visible=!1!==c.visible;v.zoomEnabled=!1!==c.zoomEnabled;v.hasNames="category"===d||!0===c.categories;v.categories= +c.categories||v.hasNames;v.names=v.names||[];v.plotLinesAndBandsGroups={};v.isLog="logarithmic"===d;v.isDatetimeAxis="datetime"===d;v.positiveValuesOnly=v.isLog&&!v.allowNegativeLog;v.isLinked=n(c.linkedTo);v.ticks={};v.labelEdge=[];v.minorTicks={};v.plotLinesAndBands=[];v.alternateBands={};v.len=0;v.minRange=v.userMinRange=c.minRange||c.maxZoom;v.range=c.range;v.offset=c.offset||0;v.stacks={};v.oldStacks={};v.stacksTouched=0;v.max=null;v.min=null;v.crosshair=C(c.crosshair,q(a.options.tooltip.crosshairs)[g? +0:1],!1);b=v.options.events;-1===k(v,a.axes)&&(g?a.axes.splice(a.xAxis.length,0,v):a.axes.push(v),a[v.coll].push(v));v.series=v.series||[];a.inverted&&!v.isZAxis&&g&&void 0===v.reversed&&(v.reversed=!0);t(b,function(a,g){E(v,g,a)});v.lin2log=c.linearToLogConverter||v.lin2log;v.isLog&&(v.val2lin=v.log2lin,v.lin2val=v.lin2log)},setOptions:function(a){this.options=x(this.defaultOptions,"yAxis"===this.coll&&this.defaultYAxisOptions,[this.defaultTopAxisOptions,this.defaultRightAxisOptions,this.defaultBottomAxisOptions, +this.defaultLeftAxisOptions][this.side],x(r[this.coll],a))},defaultLabelFormatter:function(){var g=this.axis,b=this.value,c=g.categories,k=this.dateTimeLabelFormat,e=r.lang,q=e.numericSymbols,e=e.numericSymbolMagnitude||1E3,h=q&&q.length,m,z=g.options.labels.format,g=g.isLog?Math.abs(b):g.tickInterval;if(z)m=d(z,this);else if(c)m=b;else if(k)m=a.dateFormat(k,b);else if(h&&1E3<=g)for(;h--&&void 0===m;)c=Math.pow(e,h+1),g>=c&&0===10*b%c&&null!==q[h]&&0!==b&&(m=a.numberFormat(b/c,-1)+q[h]);void 0=== +m&&(m=1E4<=Math.abs(b)?a.numberFormat(b,-1):a.numberFormat(b,-1,void 0,""));return m},getSeriesExtremes:function(){var a=this,v=a.chart;a.hasVisibleSeries=!1;a.dataMin=a.dataMax=a.threshold=null;a.softThreshold=!a.isXAxis;a.buildStacks&&a.buildStacks();e(a.series,function(g){if(g.visible||!v.options.chart.ignoreHiddenSeries){var c=g.options,d=c.threshold,k;a.hasVisibleSeries=!0;a.positiveValuesOnly&&0>=d&&(d=null);if(a.isXAxis)c=g.xData,c.length&&(g=p(c),k=H(c),B(g)||g instanceof Date||(c=b(c,B), +g=p(c)),a.dataMin=Math.min(C(a.dataMin,c[0],g),g),a.dataMax=Math.max(C(a.dataMax,c[0],k),k));else if(g.getExtremes(),k=g.dataMax,g=g.dataMin,n(g)&&n(k)&&(a.dataMin=Math.min(C(a.dataMin,g),g),a.dataMax=Math.max(C(a.dataMax,k),k)),n(d)&&(a.threshold=d),!c.softThreshold||a.positiveValuesOnly)a.softThreshold=!1}})},translate:function(a,b,c,d,k,e){var g=this.linkedParent||this,v=1,q=0,h=d?g.oldTransA:g.transA;d=d?g.oldMin:g.min;var m=g.minPixelPadding;k=(g.isOrdinal||g.isBroken||g.isLog&&k)&&g.lin2val; +h||(h=g.transA);c&&(v*=-1,q=g.len);g.reversed&&(v*=-1,q-=v*(g.sector||g.len));b?(a=(a*v+q-m)/h+d,k&&(a=g.lin2val(a))):(k&&(a=g.val2lin(a)),a=B(d)?v*(a-d)*h+q+v*m+(B(e)?h*e:0):void 0);return a},toPixels:function(a,b){return this.translate(a,!1,!this.horiz,null,!0)+(b?0:this.pos)},toValue:function(a,b){return this.translate(a-(b?0:this.pos),!0,!this.horiz,null,!0)},getPlotLinePath:function(a,b,c,d,k){var g=this.chart,v=this.left,q=this.top,e,h,m=c&&g.oldChartHeight||g.chartHeight,z=c&&g.oldChartWidth|| +g.chartWidth,A;e=this.transB;var t=function(a,g,b){if(ab)d?a=Math.min(Math.max(g,a),b):A=!0;return a};k=C(k,this.translate(a,null,null,c));a=c=Math.round(k+e);e=h=Math.round(m-k-e);B(k)?this.horiz?(e=q,h=m-this.bottom,a=c=t(a,v,v+this.width)):(a=v,c=z-this.right,e=h=t(e,q,q+this.height)):(A=!0,d=!1);return A&&!d?null:g.renderer.crispLine(["M",a,e,"L",c,h],b||1)},getLinearTickPositions:function(a,b,c){var g,v=l(Math.floor(b/a)*a);c=l(Math.ceil(c/a)*a);var d=[],k;l(v+a)===v&&(k=20);if(this.single)return[b]; +for(b=v;b<=c;){d.push(b);b=l(b+a,k);if(b===g)break;g=b}return d},getMinorTickInterval:function(){var a=this.options;return!0===a.minorTicks?C(a.minorTickInterval,"auto"):!1===a.minorTicks?null:a.minorTickInterval},getMinorTickPositions:function(){var a=this,b=a.options,c=a.tickPositions,d=a.minorTickInterval,k=[],q=a.pointRangePadding||0,h=a.min-q,q=a.max+q,m=q-h;if(m&&m/d=this.minRange,t=this.minRange,d=(t-c+b)/2,d=[b-d,C(a.min,b-d)],k&&(d[2]=this.isLog?this.log2lin(this.dataMin):this.dataMin),b=H(d),c=[b+t,C(a.max,b+t)],k&&(c[2]=this.isLog?this.log2lin(this.dataMax):this.dataMax),c=p(c),c-b=p?(r=p,f=0):b.dataMax<=p&&(w=p,x=0)),b.min= +C(N,r,b.dataMin),b.max=C(D,w,b.dataMax));q&&(b.positiveValuesOnly&&!g&&0>=Math.min(b.min,C(b.dataMin,b.min))&&a.error(10,1),b.min=l(h(b.min),15),b.max=l(h(b.max),15));b.range&&n(b.max)&&(b.userMin=b.min=N=Math.max(b.dataMin,b.minFromRange()),b.userMax=D=b.max,b.range=null);m(b,"foundExtremes");b.beforePadding&&b.beforePadding();b.adjustForMinRange();!(G||b.axisPointRange||b.usePercentage||t)&&n(b.min)&&n(b.max)&&(h=b.max-b.min)&&(!n(N)&&f&&(b.min-=h*f),!n(D)&&x&&(b.max+=h*x));B(k.softMin)&&!B(b.userMin)&& +(b.min=Math.min(b.min,k.softMin));B(k.softMax)&&!B(b.userMax)&&(b.max=Math.max(b.max,k.softMax));B(k.floor)&&(b.min=Math.max(b.min,k.floor));B(k.ceiling)&&(b.max=Math.min(b.max,k.ceiling));I&&n(b.dataMin)&&(p=p||0,!n(N)&&b.min=p?b.min=p:!n(D)&&b.max>p&&b.dataMax<=p&&(b.max=p));b.tickInterval=b.min===b.max||void 0===b.min||void 0===b.max?1:t&&!F&&u===b.linkedParent.options.tickPixelInterval?F=b.linkedParent.tickInterval:C(F,this.tickAmount?(b.max-b.min)/Math.max(this.tickAmount-1,1): +void 0,G?1:(b.max-b.min)*u/Math.max(b.len,u));A&&!g&&e(b.series,function(a){a.processData(b.min!==b.oldMin||b.max!==b.oldMax)});b.setAxisTranslation(!0);b.beforeSetTickPositions&&b.beforeSetTickPositions();b.postProcessTickInterval&&(b.tickInterval=b.postProcessTickInterval(b.tickInterval));b.pointRange&&!F&&(b.tickInterval=Math.max(b.pointRange,b.tickInterval));g=C(k.minTickInterval,b.isDatetimeAxis&&b.closestPointRange);!F&&b.tickIntervalb.tickInterval&&1E3b.max)),!!this.tickAmount));this.tickAmount||(b.tickInterval=b.unsquish());this.setTickPositions()},setTickPositions:function(){var a=this.options,b,c=a.tickPositions;b=this.getMinorTickInterval();var d=a.tickPositioner,k=a.startOnTick,q=a.endOnTick;this.tickmarkOffset=this.categories&&"between"===a.tickmarkPlacement&&1===this.tickInterval?.5:0;this.minorTickInterval="auto"===b&&this.tickInterval?this.tickInterval/ +5:b;this.single=this.min===this.max&&n(this.min)&&!this.tickAmount&&(parseInt(this.min,10)===this.min||!1!==a.allowDecimals);this.tickPositions=b=c&&c.slice();!b&&(b=this.isDatetimeAxis?this.getTimeTicks(this.normalizeTimeTickInterval(this.tickInterval,a.units),this.min,this.max,a.startOfWeek,this.ordinalPositions,this.closestPointRange,!0):this.isLog?this.getLogTickPositions(this.tickInterval,this.min,this.max):this.getLinearTickPositions(this.tickInterval,this.min,this.max),b.length>this.len&&(b= +[b[0],b.pop()],b[0]===b[1]&&(b.length=1)),this.tickPositions=b,d&&(d=d.apply(this,[this.min,this.max])))&&(this.tickPositions=b=d);this.paddedTicks=b.slice(0);this.trimTicks(b,k,q);this.isLinked||(this.single&&2>b.length&&(this.min-=.5,this.max+=.5),c||d||this.adjustTickAmount())},trimTicks:function(a,b,c){var g=a[0],d=a[a.length-1],k=this.minPointOffset||0;if(!this.isLinked){if(b&&-Infinity!==g)this.min=g;else for(;this.min-k>a[0];)a.shift();if(c)this.max=d;else for(;this.max+kb&&(this.finalTickAmt=b,b=5);this.tickAmount=b},adjustTickAmount:function(){var a=this.tickInterval,b=this.tickPositions,c=this.tickAmount,d=this.finalTickAmt,k=b&&b.length,q=C(this.threshold,this.softThreshold?0:null);if(this.hasData()){if(kc&&(this.tickInterval*= +2,this.setTickPositions());if(n(d)){for(a=c=b.length;a--;)(3===d&&1===a%2||2>=d&&0d&&(a=d)),n(c)&&(bd&&(b=d))),this.displayBtn=void 0!==a||void 0!==b,this.setExtremes(a,b,!1,void 0,{trigger:"zoom"});return!0},setAxisSize:function(){var b=this.chart,c=this.options,d=c.offsets||[0,0,0,0],k=this.horiz,q=this.width=Math.round(a.relativeLength(C(c.width,b.plotWidth-d[3]+d[1]),b.plotWidth)),e=this.height=Math.round(a.relativeLength(C(c.height, +b.plotHeight-d[0]+d[2]),b.plotHeight)),h=this.top=Math.round(a.relativeLength(C(c.top,b.plotTop+d[0]),b.plotHeight,b.plotTop)),c=this.left=Math.round(a.relativeLength(C(c.left,b.plotLeft+d[3]),b.plotWidth,b.plotLeft));this.bottom=b.chartHeight-e-h;this.right=b.chartWidth-q-c;this.len=Math.max(k?q:e,0);this.pos=k?c:h},getExtremes:function(){var a=this.isLog,b=this.lin2log;return{min:a?l(b(this.min)):this.min,max:a?l(b(this.max)):this.max,dataMin:this.dataMin,dataMax:this.dataMax,userMin:this.userMin, +userMax:this.userMax}},getThreshold:function(a){var b=this.isLog,g=this.lin2log,c=b?g(this.min):this.min,b=b?g(this.max):this.max;null===a?a=c:c>a?a=c:ba?"right":195a?"left":"center"},tickSize:function(a){var b=this.options,g=b[a+"Length"],c=C(b[a+"Width"],"tick"===a&&this.isXAxis?1:0);if(c&&g)return"inside"===b[a+"Position"]&&(g=-g),[g,c]},labelMetrics:function(){var a= +this.tickPositions&&this.tickPositions[0]||0;return this.chart.renderer.fontMetrics(this.options.labels.style&&this.options.labels.style.fontSize,this.ticks[a]&&this.ticks[a].label)},unsquish:function(){var a=this.options.labels,b=this.horiz,c=this.tickInterval,d=c,k=this.len/(((this.categories?1:0)+this.max-this.min)/c),q,h=a.rotation,m=this.labelMetrics(),z,A=Number.MAX_VALUE,t,x=function(a){a/=k||1;a=1=a)z=x(Math.abs(m.h/Math.sin(w*a))),b=z+Math.abs(a/360),b(c.step||0)&&!c.rotation&&(this.staggerLines||1)*this.len/d||!b&&(c.style&&parseInt(c.style.width,10)||k&&k-a.spacing[3]|| +.33*a.chartWidth)},renderUnsquish:function(){var a=this.chart,b=a.renderer,c=this.tickPositions,d=this.ticks,k=this.options.labels,q=this.horiz,h=this.getSlotWidth(),m=Math.max(1,Math.round(h-2*(k.padding||5))),z={},A=this.labelMetrics(),t=k.style&&k.style.textOverflow,f,F=0,l,B;I(k.rotation)||(z.rotation=k.rotation||0);e(c,function(a){(a=d[a])&&a.labelLength>F&&(F=a.labelLength)});this.maxLabelLength=F;if(this.autoRotation)F>m&&F>A.h?z.rotation=this.labelRotation:this.labelRotation=0;else if(h&& +(f={width:m+"px"},!t))for(f.textOverflow="clip",l=c.length;!q&&l--;)if(B=c[l],m=d[B].label)m.styles&&"ellipsis"===m.styles.textOverflow?m.css({textOverflow:"clip"}):d[B].labelLength>h&&m.css({width:h+"px"}),m.getBBox().height>this.len/c.length-(A.h-A.f)&&(m.specCss={textOverflow:"ellipsis"});z.rotation&&(f={width:(F>.5*a.chartHeight?.33*a.chartHeight:a.chartHeight)+"px"},t||(f.textOverflow="ellipsis"));if(this.labelAlign=k.align||this.autoLabelAlign(this.labelRotation))z.align=this.labelAlign;e(c, +function(a){var b=(a=d[a])&&a.label;b&&(b.attr(z),f&&b.css(x(f,b.specCss)),delete b.specCss,a.rotation=z.rotation)});this.tickRotCorr=b.rotCorr(A.b,this.labelRotation||0,0!==this.side)},hasData:function(){return this.hasVisibleSeries||n(this.min)&&n(this.max)&&this.tickPositions&&0=this.min&&a<=this.max)g[a]||(g[a]=new F(this,a)),d&&g[a].isNew&&g[a].render(b,!0,.1),g[a].render(b)},render:function(){var b= +this,c=b.chart,d=b.options,k=b.isLog,q=b.lin2log,h=b.isLinked,m=b.tickPositions,z=b.axisTitle,x=b.ticks,f=b.minorTicks,l=b.alternateBands,C=d.stackLabels,n=d.alternateGridColor,u=b.tickmarkOffset,G=b.axisLine,p=b.showAxis,I=D(c.renderer.globalAnimation),r,w;b.labelEdge.length=0;b.overlap=!1;e([x,f,l],function(a){t(a,function(a){a.isActive=!1})});if(b.hasData()||h)b.minorTickInterval&&!b.categories&&e(b.getMinorTickPositions(),function(a){b.renderMinorTick(a)}),m.length&&(e(m,function(a,c){b.renderTick(a, +c)}),u&&(0===b.min||b.single)&&(x[-1]||(x[-1]=new F(b,-1,null,!0)),x[-1].render(-1))),n&&e(m,function(d,g){w=void 0!==m[g+1]?m[g+1]+u:b.max-u;0===g%2&&d=h.second?0:C*Math.floor(x.getMilliseconds()/C));if(t>=h.second)x[D.hcSetSeconds](t>=h.minute? +0:C*Math.floor(x.getSeconds()/C));if(t>=h.minute)x[D.hcSetMinutes](t>=h.hour?0:C*Math.floor(x[D.hcGetMinutes]()/C));if(t>=h.hour)x[D.hcSetHours](t>=h.day?0:C*Math.floor(x[D.hcGetHours]()/C));if(t>=h.day)x[D.hcSetDate](t>=h.month?1:C*Math.floor(x[D.hcGetDate]()/C));t>=h.month&&(x[D.hcSetMonth](t>=h.year?0:C*Math.floor(x[D.hcGetMonth]()/C)),n=x[D.hcGetFullYear]());if(t>=h.year)x[D.hcSetFullYear](n-n%C);if(t===h.week)x[D.hcSetDate](x[D.hcGetDate]()-x[D.hcGetDay]()+e(b,1));n=x[D.hcGetFullYear]();b=x[D.hcGetMonth](); +var A=x[D.hcGetDate](),F=x[D.hcGetHours]();d=x.getTime();D.hcHasTimeZone&&(q=(!B||!!D.hcGetTimezoneOffset)&&(c-d>4*h.month||w(d)!==w(c)),N=w(x),x=new D(d+N));B=x.getTime();for(d=1;Bk.length&&l(k,function(a){0===a%18E5&&"000000000"===H("%H%M%S%L",a)&&(m[a]="day")})}k.info=r(a,{higherRanks:m,totalRange:t*C});return k}; +E.prototype.normalizeTimeTickInterval=function(a,d){var c=d||[["millisecond",[1,2,5,10,20,25,50,100,200,500]],["second",[1,2,5,10,15,30]],["minute",[1,2,5,10,15,30]],["hour",[1,2,3,4,6,8,12]],["day",[1,2]],["week",[1,2]],["month",[1,2,3,4,6]],["year",null]];d=c[c.length-1];var b=h[d[0]],k=d[1],e;for(e=0;er&&(!w||z<=n)&&void 0!==z&&d.push(z),z>n&& +(B=!0),z=k;else r=h(r),n=h(n),a=w?this.getMinorTickInterval():l.tickInterval,a=f("auto"===a?null:a,this._minorAutoInterval,l.tickPixelInterval/(w?5:1)*(n-r)/((w?e/this.tickPositions.length:e)||1)),a=p(a,null,D(a)),d=H(this.getLinearTickPositions(a,r,n),m),w||(this._minorAutoInterval=a/5);w||(this.tickInterval=a);return d};E.prototype.log2lin=function(a){return Math.log(a)/Math.LN10};E.prototype.lin2log=function(a){return Math.pow(10,a)}})(M);(function(a,E){var D=a.arrayMax,H=a.arrayMin,p=a.defined, +f=a.destroyObjectProperties,l=a.each,r=a.erase,n=a.merge,w=a.pick;a.PlotLineOrBand=function(a,e){this.axis=a;e&&(this.options=e,this.id=e.id)};a.PlotLineOrBand.prototype={render:function(){var f=this,e=f.axis,h=e.horiz,m=f.options,d=m.label,c=f.label,b=m.to,k=m.from,z=m.value,l=p(k)&&p(b),r=p(z),x=f.svgElem,K=!x,t=[],C=m.color,N=w(m.zIndex,0),q=m.events,t={"class":"highcharts-plot-"+(l?"band ":"line ")+(m.className||"")},A={},F=e.chart.renderer,G=l?"bands":"lines",g=e.log2lin;e.isLog&&(k=g(k),b=g(b), +z=g(z));r?(t={stroke:C,"stroke-width":m.width},m.dashStyle&&(t.dashstyle=m.dashStyle)):l&&(C&&(t.fill=C),m.borderWidth&&(t.stroke=m.borderColor,t["stroke-width"]=m.borderWidth));A.zIndex=N;G+="-"+N;(C=e.plotLinesAndBandsGroups[G])||(e.plotLinesAndBandsGroups[G]=C=F.g("plot-"+G).attr(A).add());K&&(f.svgElem=x=F.path().attr(t).add(C));if(r)t=e.getPlotLinePath(z,x.strokeWidth());else if(l)t=e.getPlotBandPath(k,b,m);else return;K&&t&&t.length?(x.attr({d:t}),q&&a.objectEach(q,function(a,b){x.on(b,function(a){q[b].apply(f, +[a])})})):x&&(t?(x.show(),x.animate({d:t})):(x.hide(),c&&(f.label=c=c.destroy())));d&&p(d.text)&&t&&t.length&&0this.max&& +e>this.max;if(m&&h)for(a&&(k=m.toString()===h.toString(),b=0),a=0;aA-h?A:A-h);else if(z)k[a]=Math.max(e,g+h+d>c?g:g+h);else return!1},C=function(a,c,d,g){var e;gc-b?e=!1:k[a]=gc-d/2?c-d-2:g-d/2;return e},p=function(a){var b=f;f=x;x=b;m=a},q=function(){!1!==t.apply(0,f)?!1!==C.apply(0,x)||m||(p(!0),q()):m?k.x=k.y=0:(p(!0),q())};(c.inverted||1q&&(h=!1);a=(e.series&& +e.series.yAxis&&e.series.yAxis.pos)+(e.plotY||0);a-=b.plotTop;c.push({target:e.isHeader?b.plotHeight+l:a,rank:e.isHeader?1:0,size:z.tt.getBBox().height+1,point:e,x:q,tt:t})}});this.cleanSplit();a.distribute(c,b.plotHeight+l);D(c,function(a){var c=a.point,d=c.series;a.tt.attr({visibility:void 0===a.pos?"hidden":"inherit",x:h||c.isHeader?a.x:c.plotX+b.plotLeft+n(f.distance,16),y:a.pos+b.plotTop,anchorX:c.isHeader?c.plotX+b.plotLeft:c.plotX+d.xAxis.pos,anchorY:c.isHeader?a.pos+b.plotTop-15:c.plotY+d.yAxis.pos})})}, +updatePosition:function(a){var e=this.chart,d=this.getLabel(),d=(this.options.positioner||this.getPosition).call(this,d.width,d.height,a);this.move(Math.round(d.x),Math.round(d.y||0),a.plotX+e.plotLeft,a.plotY+e.plotTop)},getDateFormat:function(a,m,d,c){var b=E("%m-%d %H:%M:%S.%L",m),k,h,f={millisecond:15,second:12,minute:9,hour:6,day:3},l="millisecond";for(h in e){if(a===e.week&&+E("%w",m)===d&&"00:00:00.000"===b.substr(6)){h="week";break}if(e[h]>a){h=l;break}if(f[h]&&b.substr(f[h])!=="01-01 00:00:00.000".substr(f[h]))break; +"week"!==h&&(l=h)}h&&(k=c[h]);return k},getXDateFormat:function(a,e,d){e=e.dateTimeLabelFormats;var c=d&&d.closestPointRange;return(c?this.getDateFormat(c,a.x,d.options.startOfWeek,e):e.day)||e.year},tooltipFooterHeaderFormatter:function(a,e){e=e?"footer":"header";var d=a.series,c=d.tooltipOptions,b=c.xDateFormat,k=d.xAxis,h=k&&"datetime"===k.options.type&&f(a.key),m=c[e+"Format"];h&&!b&&(b=this.getXDateFormat(a,c,k));h&&b&&D(a.point&&a.point.tooltipDateKeys||["key"],function(a){m=m.replace("{point."+ +a+"}","{point."+a+":"+b+"}")});return p(m,{point:a,series:d})},bodyFormatter:function(a){return l(a,function(a){var d=a.series.tooltipOptions;return(d[(a.point.formatPrefix||"point")+"Formatter"]||a.point.tooltipFormatter).call(a.point,d[(a.point.formatPrefix||"point")+"Format"])})}}})(M);(function(a){var E=a.addEvent,D=a.attr,H=a.charts,p=a.color,f=a.css,l=a.defined,r=a.each,n=a.extend,w=a.find,u=a.fireEvent,e=a.isObject,h=a.offset,m=a.pick,d=a.splat,c=a.Tooltip;a.Pointer=function(a,c){this.init(a, +c)};a.Pointer.prototype={init:function(a,d){this.options=d;this.chart=a;this.runChartClick=d.chart.events&&!!d.chart.events.click;this.pinchDown=[];this.lastValidTouch={};c&&(a.tooltip=new c(a,d.tooltip),this.followTouchMove=m(d.tooltip.followTouchMove,!0));this.setDOMEvents()},zoomOption:function(a){var b=this.chart,c=b.options.chart,d=c.zoomType||"",b=b.inverted;/touch/.test(a.type)&&(d=m(c.pinchType,d));this.zoomX=a=/x/.test(d);this.zoomY=d=/y/.test(d);this.zoomHor=a&&!b||d&&b;this.zoomVert=d&& +!b||a&&b;this.hasZoom=a||d},normalize:function(a,c){var b;b=a.touches?a.touches.length?a.touches.item(0):a.changedTouches[0]:a;c||(this.chartPosition=c=h(this.chart.container));return n(a,{chartX:Math.round(b.pageX-c.left),chartY:Math.round(b.pageY-c.top)})},getCoordinates:function(a){var b={xAxis:[],yAxis:[]};r(this.chart.axes,function(c){b[c.isXAxis?"xAxis":"yAxis"].push({axis:c,value:c.toValue(a[c.horiz?"chartX":"chartY"])})});return b},findNearestKDPoint:function(a,c,d){var b;r(a,function(a){var k= +!(a.noSharedTooltip&&c)&&0>a.options.findNearestPointBy.indexOf("y");a=a.searchPoint(d,k);if((k=e(a,!0))&&!(k=!e(b,!0)))var k=b.distX-a.distX,h=b.dist-a.dist,m=(a.series.group&&a.series.group.zIndex)-(b.series.group&&b.series.group.zIndex),k=0<(0!==k&&c?k:0!==h?h:0!==m?m:b.series.index>a.series.index?-1:1);k&&(b=a)});return b},getPointFromEvent:function(a){a=a.target;for(var b;a&&!b;)b=a.point,a=a.parentNode;return b},getChartCoordinatesFromPoint:function(a,c){var b=a.series,d=b.xAxis,b=b.yAxis,k= +m(a.clientX,a.plotX);if(d&&b)return c?{chartX:d.len+d.pos-k,chartY:b.len+b.pos-a.plotY}:{chartX:k+d.pos,chartY:a.plotY+b.pos}},getHoverData:function(b,c,d,h,f,l,n){var k,z=[],x=n&&n.isBoosting;h=!(!h||!b);n=c&&!c.stickyTracking?[c]:a.grep(d,function(a){return a.visible&&!(!f&&a.directTouch)&&m(a.options.enableMouseTracking,!0)&&a.stickyTracking});c=(k=h?b:this.findNearestKDPoint(n,f,l))&&k.series;k&&(f&&!c.noSharedTooltip?(n=a.grep(d,function(a){return a.visible&&!(!f&&a.directTouch)&&m(a.options.enableMouseTracking, +!0)&&!a.noSharedTooltip}),r(n,function(a){var b=w(a.points,function(a){return a.x===k.x&&!a.isNull});e(b)&&(x&&(b=a.getPoint(b)),z.push(b))})):z.push(k));return{hoverPoint:k,hoverSeries:c,hoverPoints:z}},runPointActions:function(b,c){var d=this.chart,k=d.tooltip&&d.tooltip.options.enabled?d.tooltip:void 0,e=k?k.shared:!1,h=c||d.hoverPoint,f=h&&h.series||d.hoverSeries,f=this.getHoverData(h,f,d.series,!!c||f&&f.directTouch&&this.isDirectTouch,e,b,{isBoosting:d.isBoosting}),l,h=f.hoverPoint;l=f.hoverPoints; +c=(f=f.hoverSeries)&&f.tooltipOptions.followPointer;e=e&&f&&!f.noSharedTooltip;if(h&&(h!==d.hoverPoint||k&&k.isHidden)){r(d.hoverPoints||[],function(b){-1===a.inArray(b,l)&&b.setState()});r(l||[],function(a){a.setState("hover")});if(d.hoverSeries!==f)f.onMouseOver();d.hoverPoint&&d.hoverPoint.firePointEvent("mouseOut");if(!h.series)return;h.firePointEvent("mouseOver");d.hoverPoints=l;d.hoverPoint=h;k&&k.refresh(e?l:h,b)}else c&&k&&!k.isHidden&&(h=k.getAnchor([{}],b),k.updatePosition({plotX:h[0],plotY:h[1]})); +this.unDocMouseMove||(this.unDocMouseMove=E(d.container.ownerDocument,"mousemove",function(b){var c=H[a.hoverChartIndex];if(c)c.pointer.onDocumentMouseMove(b)}));r(d.axes,function(c){var d=m(c.crosshair.snap,!0),k=d?a.find(l,function(a){return a.series[c.coll]===c}):void 0;k||!d?c.drawCrosshair(b,k):c.hideCrosshair()})},reset:function(a,c){var b=this.chart,k=b.hoverSeries,e=b.hoverPoint,h=b.hoverPoints,m=b.tooltip,f=m&&m.shared?h:e;a&&f&&r(d(f),function(b){b.series.isCartesian&&void 0===b.plotX&& +(a=!1)});if(a)m&&f&&(m.refresh(f),e&&(e.setState(e.state,!0),r(b.axes,function(a){a.crosshair&&a.drawCrosshair(null,e)})));else{if(e)e.onMouseOut();h&&r(h,function(a){a.setState()});if(k)k.onMouseOut();m&&m.hide(c);this.unDocMouseMove&&(this.unDocMouseMove=this.unDocMouseMove());r(b.axes,function(a){a.hideCrosshair()});this.hoverX=b.hoverPoints=b.hoverPoint=null}},scaleGroups:function(a,c){var b=this.chart,d;r(b.series,function(k){d=a||k.getPlotBox();k.xAxis&&k.xAxis.zoomEnabled&&k.group&&(k.group.attr(d), +k.markerGroup&&(k.markerGroup.attr(d),k.markerGroup.clip(c?b.clipRect:null)),k.dataLabelsGroup&&k.dataLabelsGroup.attr(d))});b.clipRect.attr(c||b.clipBox)},dragStart:function(a){var b=this.chart;b.mouseIsDown=a.type;b.cancelClick=!1;b.mouseDownX=this.mouseDownX=a.chartX;b.mouseDownY=this.mouseDownY=a.chartY},drag:function(a){var b=this.chart,c=b.options.chart,d=a.chartX,e=a.chartY,h=this.zoomHor,m=this.zoomVert,f=b.plotLeft,l=b.plotTop,n=b.plotWidth,q=b.plotHeight,A,F=this.selectionMarker,G=this.mouseDownX, +g=this.mouseDownY,v=c.panKey&&a[c.panKey+"Key"];F&&F.touch||(df+n&&(d=f+n),el+q&&(e=l+q),this.hasDragged=Math.sqrt(Math.pow(G-d,2)+Math.pow(g-e,2)),10u.max&&(f=u.max-x,v=!0);v?(F-=.8*(F-m[b][0]),q||(g-=.8*(g-m[b][1])),l()):m[b]=[F,g];C||(h[b]=w-r,h[n]=x);h=C?1/t:t;e[n]=x;e[b]=f;p[C?a?"scaleY":"scaleX":"scale"+k]=t;p["translate"+k]=h*r+(F-h*A)},pinch:function(a){var n=this,r=n.chart,u=n.pinchDown,e=a.touches,h=e.length,m=n.lastValidTouch, +d=n.hasZoom,c=n.selectionMarker,b={},k=1===h&&(n.inClass(a.target,"highcharts-tracker")&&r.runTrackerClick||n.runChartClick),z={};1c-6&&h(q||c.spacingBox.width-2*t-d.x)&&(this.itemX=t,this.itemY+=F+this.lastLineHeight+A,this.lastLineHeight=0);this.maxItemWidth=Math.max(this.maxItemWidth,m);this.lastItemY=F+this.itemY+A;this.lastLineHeight=Math.max(b,this.lastLineHeight);a._legendItemPos=[this.itemX,this.itemY];e?this.itemX+=m:(this.itemY+=F+b+A,this.lastLineHeight=b);this.offsetWidth=q||Math.max((e?this.itemX-t-(a.checkbox?0:p):m)+t,this.offsetWidth)}, +getAllItems:function(){var a=[];f(this.chart.series,function(c){var b=c&&c.options;c&&w(b.showInLegend,p(b.linkedTo)?!1:void 0,!0)&&(a=a.concat(c.legendItems||("point"===b.legendType?c.data:c)))});return a},getAlignment:function(){var a=this.options;return a.floating?"":a.align.charAt(0)+a.verticalAlign.charAt(0)+a.layout.charAt(0)},adjustMargins:function(a,c){var b=this.chart,d=this.options,e=this.getAlignment();e&&f([/(lth|ct|rth)/,/(rtv|rm|rbv)/,/(rbh|cb|lbh)/,/(lbv|lm|ltv)/],function(k,h){k.test(e)&& +!p(a[h])&&(b[r[h]]=Math.max(b[r[h]],b.legend[(h+1)%2?"legendHeight":"legendWidth"]+[1,-1,-1,1][h]*d[h%2?"x":"y"]+w(d.margin,12)+c[h]+(0===h?b.titleOffset+b.options.title.margin:0)))})},render:function(){var a=this,c=a.chart,b=c.renderer,k=a.group,h,m,l,x,p=a.box,t=a.options,C=a.padding;a.itemX=C;a.itemY=a.initialItemY;a.offsetWidth=0;a.lastItemY=0;k||(a.group=k=b.g("legend").attr({zIndex:7}).add(),a.contentGroup=b.g().attr({zIndex:1}).add(k),a.scrollGroup=b.g().add(a.contentGroup));a.renderTitle(); +h=a.getAllItems();e(h,function(a,b){return(a.options&&a.options.legendIndex||0)-(b.options&&b.options.legendIndex||0)});t.reversed&&h.reverse();a.allItems=h;a.display=m=!!h.length;a.lastLineHeight=0;f(h,function(b){a.renderItem(b)});l=(t.width||a.offsetWidth)+C;x=a.lastItemY+a.lastLineHeight+a.titleHeight;x=a.handleOverflow(x);x+=C;p||(a.box=p=b.rect().addClass("highcharts-legend-box").attr({r:t.borderRadius}).add(k),p.isNew=!0);p.attr({stroke:t.borderColor,"stroke-width":t.borderWidth||0,fill:t.backgroundColor|| +"none"}).shadow(t.shadow);0b&&!1!==t.enabled?(this.clipHeight=l=Math.max(b-20-this.titleHeight-m,0),this.currentPage=w(this.currentPage,1),this.fullHeight=a,f(G,function(a,b){var c=a._legendItemPos[1],d=Math.round(a.legendItem.getBBox().height),g=A.length;if(!g||c-A[g-1]>l&&(F||c)!==A[g-1])A.push(F||c),g++;a.pageIx=g-1;F&&(G[b-1].pageIx=g-1);b===G.length-1&&c+d-A[g-1]>l&&(A.push(c),a.pageIx=g);c!==F&&(F=c)}),n||(n=c.clipRect=d.clipRect(0,m,9999,0),c.contentGroup.clip(n)),g(l),q||(this.nav=q=d.g().attr({zIndex:1}).add(this.group), +this.up=d.symbol("triangle",0,0,r,r).on("click",function(){c.scroll(-1,p)}).add(q),this.pager=d.text("",15,10).addClass("highcharts-legend-navigation").css(t.style).add(q),this.down=d.symbol("triangle-down",0,0,r,r).on("click",function(){c.scroll(1,p)}).add(q)),c.scroll(0),a=b):q&&(g(),this.nav=q.destroy(),this.scrollGroup.attr({translateY:1}),this.clipHeight=0);return a},scroll:function(a,c){var b=this.pages,d=b.length;a=this.currentPage+a;var e=this.clipHeight,h=this.options.navigation,f=this.pager, +m=this.padding;a>d&&(a=d);0b&&(f=typeof a[0],"string"===f?e.name=a[0]:"number"===f&&(e.x=a[0]),k++);l=d.value;)d=h[++f];d&&d.color&&!this.options.color&&(this.color=d.color);return d},destroy:function(){var a=this.series.chart,h=a.hoverPoints,f;a.pointCount--;h&&(this.setState(),p(h,this),h.length||(a.hoverPoints=null));if(this===a.hoverPoint)this.onMouseOut();if(this.graphic||this.dataLabel)u(this),this.destroyElements();this.legendItem&&a.legend.destroyItem(this);for(f in this)this[f]=null},destroyElements:function(){for(var a=["graphic","dataLabel", +"dataLabelUpper","connector","shadowGroup"],h,f=6;f--;)h=a[f],this[h]&&(this[h]=this[h].destroy())},getLabelConfig:function(){return{x:this.category,y:this.y,color:this.color,colorIndex:this.colorIndex,key:this.name||this.category,series:this.series,point:this,percentage:this.percentage,total:this.total||this.stackTotal}},tooltipFormatter:function(a){var e=this.series,f=e.tooltipOptions,d=w(f.valueDecimals,""),c=f.valuePrefix||"",b=f.valueSuffix||"";D(e.pointArrayMap||["y"],function(e){e="{point."+ +e;if(c||b)a=a.replace(e+"}",c+e+"}"+b);a=a.replace(e+"}",e+":,."+d+"f}")});return l(a,{point:this,series:this.series})},firePointEvent:function(a,h,m){var d=this,c=this.series.options;(c.point.events[a]||d.options&&d.options.events&&d.options.events[a])&&this.importEvents();"click"===a&&c.allowPointSelect&&(m=function(a){d.select&&d.select(null,a.ctrlKey||a.metaKey||a.shiftKey)});f(this,a,h,m)},visible:!0}})(M);(function(a){var E=a.addEvent,D=a.animObject,H=a.arrayMax,p=a.arrayMin,f=a.correctFloat, +l=a.Date,r=a.defaultOptions,n=a.defaultPlotOptions,w=a.defined,u=a.each,e=a.erase,h=a.extend,m=a.fireEvent,d=a.grep,c=a.isArray,b=a.isNumber,k=a.isString,z=a.merge,B=a.objectEach,I=a.pick,x=a.removeEvent,K=a.splat,t=a.SVGElement,C=a.syncTimeout,N=a.win;a.Series=a.seriesType("line",null,{lineWidth:2,allowPointSelect:!1,showCheckbox:!1,animation:{duration:1E3},events:{},marker:{lineWidth:0,lineColor:"#ffffff",radius:4,states:{hover:{animation:{duration:50},enabled:!0,radiusPlus:2,lineWidthPlus:1},select:{fillColor:"#cccccc", +lineColor:"#000000",lineWidth:2}}},point:{events:{}},dataLabels:{align:"center",formatter:function(){return null===this.y?"":a.numberFormat(this.y,-1)},style:{fontSize:"11px",fontWeight:"bold",color:"contrast",textOutline:"1px contrast"},verticalAlign:"bottom",x:0,y:0,padding:5},cropThreshold:300,pointRange:0,softThreshold:!0,states:{hover:{animation:{duration:50},lineWidthPlus:1,marker:{},halo:{size:10,opacity:.25}},select:{marker:{}}},stickyTracking:!0,turboThreshold:1E3,findNearestPointBy:"x"}, +{isCartesian:!0,pointClass:a.Point,sorted:!0,requireSorting:!0,directTouch:!1,axisTypes:["xAxis","yAxis"],colorCounter:0,parallelArrays:["x","y"],coll:"series",init:function(a,b){var c=this,d,g=a.series,e;c.chart=a;c.options=b=c.setOptions(b);c.linkedSeries=[];c.bindAxes();h(c,{name:b.name,state:"",visible:!1!==b.visible,selected:!0===b.selected});d=b.events;B(d,function(a,b){E(c,b,a)});if(d&&d.click||b.point&&b.point.events&&b.point.events.click||b.allowPointSelect)a.runTrackerClick=!0;c.getColor(); +c.getSymbol();u(c.parallelArrays,function(a){c[a+"Data"]=[]});c.setData(b.data,!1);c.isCartesian&&(a.hasCartesianSeries=!0);g.length&&(e=g[g.length-1]);c._i=I(e&&e._i,-1)+1;a.orderSeries(this.insert(g))},insert:function(a){var c=this.options.index,d;if(b(c)){for(d=a.length;d--;)if(c>=I(a[d].options.index,a[d]._i)){a.splice(d+1,0,this);break}-1===d&&a.unshift(this);d+=1}else a.push(this);return I(d,a.length-1)},bindAxes:function(){var b=this,c=b.options,d=b.chart,e;u(b.axisTypes||[],function(g){u(d[g], +function(a){e=a.options;if(c[g]===e.index||void 0!==c[g]&&c[g]===e.id||void 0===c[g]&&0===e.index)b.insert(a.series),b[g]=a,a.isDirty=!0});b[g]||b.optionalAxis===g||a.error(18,!0)})},updateParallelArrays:function(a,c){var d=a.series,e=arguments,g=b(c)?function(b){var g="y"===b&&d.toYData?d.toYData(a):a[b];d[b+"Data"][c]=g}:function(a){Array.prototype[c].apply(d[a+"Data"],Array.prototype.slice.call(e,2))};u(d.parallelArrays,g)},autoIncrement:function(){var b=this.options,c=this.xIncrement,d,e=b.pointIntervalUnit, +g=0,c=I(c,b.pointStart,0);this.pointInterval=d=I(this.pointInterval,b.pointInterval,1);e&&(b=new l(c),"day"===e?b=+b[l.hcSetDate](b[l.hcGetDate]()+d):"month"===e?b=+b[l.hcSetMonth](b[l.hcGetMonth]()+d):"year"===e&&(b=+b[l.hcSetFullYear](b[l.hcGetFullYear]()+d)),l.hcHasTimeZone&&(g=a.getTZOffset(b)-a.getTZOffset(c)),d=b-c+g);this.xIncrement=c+d;return c},setOptions:function(a){var b=this.chart,c=b.options,d=c.plotOptions,g=(b.userOptions||{}).plotOptions||{},e=d[this.type];this.userOptions=a;b=z(e, +d.series,a);this.tooltipOptions=z(r.tooltip,r.plotOptions.series&&r.plotOptions.series.tooltip,r.plotOptions[this.type].tooltip,c.tooltip.userOptions,d.series&&d.series.tooltip,d[this.type].tooltip,a.tooltip);this.stickyTracking=I(a.stickyTracking,g[this.type]&&g[this.type].stickyTracking,g.series&&g.series.stickyTracking,this.tooltipOptions.shared&&!this.noSharedTooltip?!0:b.stickyTracking);null===e.marker&&delete b.marker;this.zoneAxis=b.zoneAxis;a=this.zones=(b.zones||[]).slice();!b.negativeColor&& +!b.negativeFillColor||b.zones||a.push({value:b[this.zoneAxis+"Threshold"]||b.threshold||0,className:"highcharts-negative",color:b.negativeColor,fillColor:b.negativeFillColor});a.length&&w(a[a.length-1].value)&&a.push({color:this.color,fillColor:this.fillColor});return b},getCyclic:function(a,b,c){var d,g=this.chart,e=this.userOptions,k=a+"Index",h=a+"Counter",q=c?c.length:I(g.options.chart[a+"Count"],g[a+"Count"]);b||(d=I(e[k],e["_"+k]),w(d)||(g.series.length||(g[h]=0),e["_"+k]=d=g[h]%q,g[h]+=1), +c&&(b=c[d]));void 0!==d&&(this[k]=d);this[a]=b},getColor:function(){this.options.colorByPoint?this.options.color=null:this.getCyclic("color",this.options.color||n[this.type].color,this.chart.options.colors)},getSymbol:function(){this.getCyclic("symbol",this.options.marker.symbol,this.chart.options.symbols)},drawLegendSymbol:a.LegendSymbolMixin.drawLineMarker,setData:function(d,e,h,f){var g=this,q=g.points,m=q&&q.length||0,l,A=g.options,t=g.chart,x=null,n=g.xAxis,p=A.turboThreshold,z=this.xData,F= +this.yData,C=(l=g.pointArrayMap)&&l.length;d=d||[];l=d.length;e=I(e,!0);if(!1!==f&&l&&m===l&&!g.cropped&&!g.hasGroupedData&&g.visible)u(d,function(a,b){q[b].update&&a!==A.data[b]&&q[b].update(a,!1,null,!1)});else{g.xIncrement=null;g.colorCounter=0;u(this.parallelArrays,function(a){g[a+"Data"].length=0});if(p&&l>p){for(h=0;null===x&&hf||this.forceCrop))if(c[e-1]z)c=[],d=[];else if(c[0]z)g=this.cropData(this.xData,this.yData,p,z),c=g.xData,d=g.yData,g=g.start,k=!0;for(f=c.length|| +1;--f;)e=x?m(c[f])-m(c[f-1]):c[f]-c[f-1],0e&&n&&(a.error(15),n=!1);this.cropped=k;this.cropStart=g;this.processedXData=c;this.processedYData=d;this.closestPointRange=h},cropData:function(a,b,c,d){var g=a.length,e=0,k=g,h=I(this.cropShoulder,1),f;for(f=0;f=c){e=Math.max(0,f-h);break}for(c=f;cd){k=c+h;break}return{xData:a.slice(e,k),yData:b.slice(e,k),start:e,end:k}},generatePoints:function(){var a=this.options,b=a.data,c=this.data,d,g=this.processedXData, +e=this.processedYData,k=this.pointClass,h=g.length,f=this.cropStart||0,m,l=this.hasGroupedData,a=a.keys,t,x=[],n;c||l||(c=[],c.length=b.length,c=this.data=c);a&&l&&(this.options.keys=!1);for(n=0;n=f&&(e[n-1]||l)<=q,m&&l)if(m=t.length)for(;m--;)"number"===typeof t[m]&&(g[h++]=t[m]);else g[h++]=t;this.dataMin= +p(g);this.dataMax=H(g)},translate:function(){this.processedXData||this.processData();this.generatePoints();var a=this.options,c=a.stacking,d=this.xAxis,e=d.categories,g=this.yAxis,k=this.points,h=k.length,m=!!this.modifyValue,l=a.pointPlacement,t="between"===l||b(l),n=a.threshold,x=a.startFromThreshold?n:0,p,z,C,r,u=Number.MAX_VALUE;"between"===l&&(l=.5);b(l)&&(l*=I(a.pointRange||d.pointRange));for(a=0;a=K&&(B.isNull=!0);B.plotX=p=f(Math.min(Math.max(-1E5,d.translate(N,0,0,0,1,l,"flags"===this.type)),1E5));c&&this.visible&&!B.isNull&&D&&D[N]&&(r=this.getStackIndicator(r,N,this.index),E=D[N],K=E.points[r.key],z=K[0],K=K[1],z===x&&r.key===D[N].base&&(z=I(n,g.min)),g.positiveValuesOnly&&0>=z&&(z=null),B.total=B.stackTotal=E.total,B.percentage=E.total&&B.y/E.total*100,B.stackY=K,E.setOffset(this.pointXOffset||0,this.barW||0));B.yBottom=w(z)?g.translate(z,0,1,0,1): +null;m&&(K=this.modifyValue(K,B));B.plotY=z="number"===typeof K&&Infinity!==K?Math.min(Math.max(-1E5,g.translate(K,0,1,0,1)),1E5):void 0;B.isInside=void 0!==z&&0<=z&&z<=g.len&&0<=p&&p<=d.len;B.clientX=t?f(d.translate(N,0,0,0,1,l)):p;B.negative=B.y<(n||0);B.category=e&&void 0!==e[B.x]?e[B.x]:B.x;B.isNull||(void 0!==C&&(u=Math.min(u,Math.abs(p-C))),C=p);B.zone=this.zones.length&&B.getZone()}this.closestPointRangePx=u},getValidPoints:function(a,b){var c=this.chart;return d(a||this.points||[],function(a){return b&& +!c.isInsidePlot(a.plotX,a.plotY,c.inverted)?!1:!a.isNull})},setClip:function(a){var b=this.chart,c=this.options,d=b.renderer,g=b.inverted,e=this.clipBox,k=e||b.clipBox,h=this.sharedClipKey||["_sharedClip",a&&a.duration,a&&a.easing,k.height,c.xAxis,c.yAxis].join(),f=b[h],q=b[h+"m"];f||(a&&(k.width=0,g&&(k.x=b.plotSizeX),b[h+"m"]=q=d.clipRect(g?b.plotSizeX+99:-99,g?-b.plotLeft:-b.plotTop,99,g?b.chartWidth:b.chartHeight)),b[h]=f=d.clipRect(k),f.count={length:0});a&&!f.count[this.index]&&(f.count[this.index]= +!0,f.count.length+=1);!1!==c.clip&&(this.group.clip(a||e?f:b.clipRect),this.markerGroup.clip(q),this.sharedClipKey=h);a||(f.count[this.index]&&(delete f.count[this.index],--f.count.length),0===f.count.length&&h&&b[h]&&(e||(b[h]=b[h].destroy()),b[h+"m"]&&(b[h+"m"]=b[h+"m"].destroy())))},animate:function(a){var b=this.chart,c=D(this.options.animation),d;a?this.setClip(c):(d=this.sharedClipKey,(a=b[d])&&a.animate({width:b.plotSizeX,x:0},c),b[d+"m"]&&b[d+"m"].animate({width:b.plotSizeX+99,x:0},c),this.animate= +null)},afterAnimate:function(){this.setClip();m(this,"afterAnimate");this.finishedAnimating=!0},drawPoints:function(){var a=this.points,b=this.chart,c,d,g,e,k=this.options.marker,h,f,l,m=this[this.specialGroup]||this.markerGroup,t,n=I(k.enabled,this.xAxis.isRadial?!0:null,this.closestPointRangePx>=2*k.radius);if(!1!==k.enabled||this._hasPointMarkers)for(c=0;ce&&b.shadow));k&&(k.startX=c.xMap,k.isArea=c.isArea)})}, +applyZones:function(){var a=this,b=this.chart,c=b.renderer,d=this.zones,e,k,h=this.clips||[],f,m=this.graph,l=this.area,t=Math.max(b.chartWidth,b.chartHeight),n=this[(this.zoneAxis||"y")+"Axis"],x,p,z=b.inverted,C,r,w,B,K=!1;d.length&&(m||l)&&n&&void 0!==n.min&&(p=n.reversed,C=n.horiz,m&&m.hide(),l&&l.hide(),x=n.getExtremes(),u(d,function(d,g){e=p?C?b.plotWidth:0:C?0:n.toPixels(x.min);e=Math.min(Math.max(I(k,e),0),t);k=Math.min(Math.max(Math.round(n.toPixels(I(d.value,x.max),!0)),0),t);K&&(e=k=n.toPixels(x.max)); +r=Math.abs(e-k);w=Math.min(e,k);B=Math.max(e,k);n.isXAxis?(f={x:z?B:w,y:0,width:r,height:t},C||(f.x=b.plotHeight-f.x)):(f={x:0,y:z?B:w,width:t,height:r},C&&(f.y=b.plotWidth-f.y));z&&c.isVML&&(f=n.isXAxis?{x:0,y:p?w:B,height:f.width,width:b.chartWidth}:{x:f.y-b.plotLeft-b.spacingBox.x,y:0,width:f.height,height:b.chartHeight});h[g]?h[g].animate(f):(h[g]=c.clipRect(f),m&&a["zone-graph-"+g].clip(h[g]),l&&a["zone-area-"+g].clip(h[g]));K=d.value>x.max}),this.clips=h)},invertGroups:function(a){function b(){u(["group", +"markerGroup"],function(b){c[b]&&(d.renderer.isVML&&c[b].attr({width:c.yAxis.len,height:c.xAxis.len}),c[b].width=c.yAxis.len,c[b].height=c.xAxis.len,c[b].invert(a))})}var c=this,d=c.chart,e;c.xAxis&&(e=E(d,"resize",b),E(c,"destroy",e),b(a),c.invertGroups=b)},plotGroup:function(a,b,c,d,e){var g=this[a],k=!g;k&&(this[a]=g=this.chart.renderer.g().attr({zIndex:d||.1}).add(e));g.addClass("highcharts-"+b+" highcharts-series-"+this.index+" highcharts-"+this.type+"-series "+(w(this.colorIndex)?"highcharts-color-"+ +this.colorIndex+" ":"")+(this.options.className||"")+(g.hasClass("highcharts-tracker")?" highcharts-tracker":""),!0);g.attr({visibility:c})[k?"attr":"animate"](this.getPlotBox());return g},getPlotBox:function(){var a=this.chart,b=this.xAxis,c=this.yAxis;a.inverted&&(b=c,c=this.xAxis);return{translateX:b?b.left:a.plotLeft,translateY:c?c.top:a.plotTop,scaleX:1,scaleY:1}},render:function(){var a=this,b=a.chart,c,d=a.options,e=!!a.animate&&b.renderer.isSVG&&D(d.animation).duration,k=a.visible?"inherit": +"hidden",h=d.zIndex,f=a.hasRendered,m=b.seriesGroup,l=b.inverted;c=a.plotGroup("group","series",k,h,m);a.markerGroup=a.plotGroup("markerGroup","markers",k,h,m);e&&a.animate(!0);c.inverted=a.isCartesian?l:!1;a.drawGraph&&(a.drawGraph(),a.applyZones());a.drawDataLabels&&a.drawDataLabels();a.visible&&a.drawPoints();a.drawTracker&&!1!==a.options.enableMouseTracking&&a.drawTracker();a.invertGroups(l);!1===d.clip||a.sharedClipKey||f||c.clip(b.clipRect);e&&a.animate();f||(a.animationTimeout=C(function(){a.afterAnimate()}, +e));a.isDirty=!1;a.hasRendered=!0},redraw:function(){var a=this.chart,b=this.isDirty||this.isDirtyData,c=this.group,d=this.xAxis,e=this.yAxis;c&&(a.inverted&&c.attr({width:a.plotWidth,height:a.plotHeight}),c.animate({translateX:I(d&&d.left,a.plotLeft),translateY:I(e&&e.top,a.plotTop)}));this.translate();this.render();b&&delete this.kdTree},kdAxisArray:["clientX","plotY"],searchPoint:function(a,b){var c=this.xAxis,d=this.yAxis,e=this.chart.inverted;return this.searchKDTree({clientX:e?c.len-a.chartY+ +c.pos:a.chartX-c.pos,plotY:e?d.len-a.chartX+d.pos:a.chartY-d.pos},b)},buildKDTree:function(){function a(c,d,e){var g,k;if(k=c&&c.length)return g=b.kdAxisArray[d%e],c.sort(function(a,b){return a[g]-b[g]}),k=Math.floor(k/2),{point:c[k],left:a(c.slice(0,k),d+1,e),right:a(c.slice(k+1),d+1,e)}}this.buildingKdTree=!0;var b=this,c=-1l?"left":"right";t=0>l?"right":"left";b[q]&&(q=c(a,b[q],g+1,f),n=q[h]x;)q--;this.updateParallelArrays(m,"splice",q,0,0);this.updateParallelArrays(m,q);g&&m.name&&(g[x]=m.name);l.splice(q,0,a);n&&(this.data.splice(q,0,null),this.processData());"point"===e.legendType&&this.generatePoints();c&&(h[0]&&h[0].remove?h[0].remove(!1):(h.shift(),this.updateParallelArrays(m,"shift"),l.shift()));this.isDirtyData=this.isDirty=!0;b&&f.redraw(d)},removePoint:function(a,b,c){var d=this,e=d.data,h=e[a],f=d.points,g=d.chart,l=function(){f&&f.length===e.length&& +f.splice(a,1);e.splice(a,1);d.options.data.splice(a,1);d.updateParallelArrays(h||{series:d},"splice",a,1);h&&h.destroy();d.isDirty=!0;d.isDirtyData=!0;b&&g.redraw()};x(c,g);b=k(b,!0);h?h.firePointEvent("remove",null,l):l()},remove:function(a,b,c){function d(){e.destroy();h.isDirtyLegend=h.isDirtyBox=!0;h.linkSeries();k(a,!0)&&h.redraw(b)}var e=this,h=e.chart;!1!==c?u(e,"remove",null,d):d()},update:function(a,b){var d=this,e=d.chart,h=d.userOptions,f=d.oldType||d.type,l=a.type||h.type||e.options.chart.type, +g=I[f].prototype,m,n=["group","markerGroup","dataLabelsGroup"],t=["navigatorSeries","baseSeries"],x=d.finishedAnimating&&{animation:!1};if(Object.keys&&"data"===Object.keys(a).toString())return this.setData(a.data,b);t=n.concat(t);r(t,function(a){t[a]=d[a];delete d[a]});a=c(h,x,{index:d.index,pointStart:d.xData[0]},{data:d.options.data},a);d.remove(!1,null,!1);for(m in g)d[m]=void 0;w(d,I[l||f].prototype);r(t,function(a){d[a]=t[a]});d.init(e,a);a.zIndex!==h.zIndex&&r(n,function(b){d[b]&&d[b].attr({zIndex:a.zIndex})}); +d.oldType=f;e.linkSeries();k(b,!0)&&e.redraw(!1)}});w(H.prototype,{update:function(a,b){var d=this.chart;a=d.options[this.coll][this.options.index]=c(this.userOptions,a);this.destroy(!0);this.init(d,w(a,{events:void 0}));d.isDirtyBox=!0;k(b,!0)&&d.redraw()},remove:function(a){for(var b=this.chart,c=this.coll,e=this.series,h=e.length;h--;)e[h]&&e[h].remove(!1);n(b.axes,this);n(b[c],this);d(b.options[c])?b.options[c].splice(this.options.index,1):delete b.options[c];r(b[c],function(a,b){a.options.index= +b});this.destroy();b.isDirtyBox=!0;k(a,!0)&&b.redraw()},setTitle:function(a,b){this.update({title:a},b)},setCategories:function(a,b){this.update({categories:a},b)}})})(M);(function(a){var E=a.color,D=a.each,H=a.map,p=a.pick,f=a.Series,l=a.seriesType;l("area","line",{softThreshold:!1,threshold:0},{singleStacks:!1,getStackPoints:function(f){var l=[],r=[],u=this.xAxis,e=this.yAxis,h=e.stacks[this.stackKey],m={},d=this.index,c=e.series,b=c.length,k,z=p(e.options.reversedStacks,!0)?1:-1,B;f=f||this.points; +if(this.options.stacking){for(B=0;Ba&&w>l?(w=Math.max(a,l),e=2*l-w):wp&&e>l?(e=Math.max(p,l),w=2*l-e):e=Math.abs(h)&&.5a.closestPointRange*a.xAxis.transA,d=a.borderWidth=r(f.borderWidth,d?0:1),c=a.yAxis,b=f.threshold,k=a.translatedThreshold=c.getThreshold(b),l=r(f.minPointLength,5),p=a.getColumnMetrics(),u=p.width,x=a.barW=Math.max(u,1+2*d),w=a.pointXOffset=p.offset;h.inverted&&(k-=.5);f.pointPadding&&(x=Math.ceil(x));n.prototype.translate.apply(a);H(a.points,function(d){var e=r(d.yBottom,k),f=999+Math.abs(e),f=Math.min(Math.max(-f,d.plotY),c.len+f),m=d.plotX+w,n=x,t=Math.min(f,e),p,g=Math.max(f,e)-t;l&& +Math.abs(g)l?e-l:k-(p?l:0));d.barX=m;d.pointWidth=u;d.tooltipPos=h.inverted?[c.len+c.pos-h.plotLeft-f,a.xAxis.len-m-n/2,g]:[m+n/2,f+c.pos-h.plotTop,g];d.shapeType="rect";d.shapeArgs=a.crispCol.apply(a,d.isNull?[m,k,n,0]:[m,t,n,g])})},getSymbol:a.noop,drawLegendSymbol:a.LegendSymbolMixin.drawRectangle,drawGraph:function(){this.group[this.dense?"addClass":"removeClass"]("highcharts-dense-data")}, +pointAttribs:function(a,f){var e=this.options,d,c=this.pointAttrToOptions||{};d=c.stroke||"borderColor";var b=c["stroke-width"]||"borderWidth",k=a&&a.color||this.color,h=a&&a[d]||e[d]||this.color||k,n=a&&a[b]||e[b]||this[b]||0,c=e.dashStyle;a&&this.zones.length&&(k=a.getZone(),k=a.options.color||k&&k.color||this.color);f&&(a=l(e.states[f],a.options.states&&a.options.states[f]||{}),f=a.brightness,k=a.color||void 0!==f&&D(k).brighten(a.brightness).get()||k,h=a[d]||h,n=a[b]||n,c=a.dashStyle||c);d={fill:k, +stroke:h,"stroke-width":n};c&&(d.dashstyle=c);return d},drawPoints:function(){var a=this,h=this.chart,m=a.options,d=h.renderer,c=m.animationLimit||250,b;H(a.points,function(e){var k=e.graphic;if(f(e.plotY)&&null!==e.y){b=e.shapeArgs;if(k)k[h.pointCounte;++e)h=w[e],a=2>e||2===e&&/%$/.test(h),w[e]=p(h,[n,l,u,w[2]][e])+(a?r:0);w[3]>w[2]&&(w[3]=w[2]);return w},getStartAndEndRadians:function(a,l){a=D(a)?a:0;l=D(l)&&l>a&&360>l-a?l:a+360;return{start:E*(a+-90),end:E*(l+-90)}}}})(M);(function(a){var E=a.addEvent,D=a.CenteredSeriesMixin,H=a.defined,p=a.each,f=a.extend,l=D.getStartAndEndRadians,r=a.inArray,n=a.noop,w=a.pick,u=a.Point,e=a.Series,h=a.seriesType,m=a.setAnimation;h("pie","line",{center:[null,null],clip:!1,colorByPoint:!0,dataLabels:{distance:30, +enabled:!0,formatter:function(){return this.point.isNull?void 0:this.point.name},x:0},ignoreHiddenPoint:!0,legendType:"point",marker:null,size:null,showInLegend:!1,slicedOffset:10,stickyTracking:!1,tooltip:{followPointer:!0},borderColor:"#ffffff",borderWidth:1,states:{hover:{brightness:.1,shadow:!1}}},{isCartesian:!1,requireSorting:!1,directTouch:!0,noSharedTooltip:!0,trackerGroups:["group","dataLabelsGroup"],axisTypes:[],pointAttribs:a.seriesTypes.column.prototype.pointAttribs,animate:function(a){var c= +this,b=c.points,d=c.startAngleRad;a||(p(b,function(a){var b=a.graphic,e=a.shapeArgs;b&&(b.attr({r:a.startR||c.center[3]/2,start:d,end:d}),b.animate({r:e.r,start:e.start,end:e.end},c.options.animation))}),c.animate=null)},updateTotals:function(){var a,c=0,b=this.points,e=b.length,f,h=this.options.ignoreHiddenPoint;for(a=0;a1.5*Math.PI?m-=2*Math.PI:m<-Math.PI/2&&(m+=2*Math.PI);G.slicedTranslation={translateX:Math.round(Math.cos(m)*d),translateY:Math.round(Math.sin(m)*d)};h=Math.cos(m)*a[2]/ +2;u=Math.sin(m)*a[2]/2;G.tooltipPos=[a[0]+.7*h,a[1]+.7*u];G.half=m<-Math.PI/2||m>Math.PI/2?1:0;G.angle=m;f=Math.min(e,G.labelDistance/5);G.labelPos=[a[0]+h+Math.cos(m)*G.labelDistance,a[1]+u+Math.sin(m)*G.labelDistance,a[0]+h+Math.cos(m)*f,a[1]+u+Math.sin(m)*f,a[0]+h,a[1]+u,0>G.labelDistance?"center":G.half?"right":"left",m]}},drawGraph:null,drawPoints:function(){var a=this,c=a.chart.renderer,b,e,h,l,m=a.options.shadow;m&&!a.shadowGroup&&(a.shadowGroup=c.g("shadow").add(a.group));p(a.points,function(d){e= +d.graphic;if(d.isNull)e&&(d.graphic=e.destroy());else{l=d.shapeArgs;b=d.getTranslate();var k=d.shadowGroup;m&&!k&&(k=d.shadowGroup=c.g("shadow").add(a.shadowGroup));k&&k.attr(b);h=a.pointAttribs(d,d.selected&&"select");e?e.setRadialReference(a.center).attr(h).animate(f(l,b)):(d.graphic=e=c[d.shapeType](l).setRadialReference(a.center).attr(b).add(a.group),d.visible||e.attr({visibility:"hidden"}),e.attr(h).attr({"stroke-linejoin":"round"}).shadow(m,k));e.addClass(d.getClassName())}})},searchPoint:n, +sortByAngle:function(a,c){a.sort(function(a,d){return void 0!==a.angle&&(d.angle-a.angle)*c})},drawLegendSymbol:a.LegendSymbolMixin.drawRectangle,getCenter:D.getCenter,getSymbol:n},{init:function(){u.prototype.init.apply(this,arguments);var a=this,c;a.name=w(a.name,"Slice");c=function(b){a.slice("select"===b.type)};E(a,"select",c);E(a,"unselect",c);return a},isValid:function(){return a.isNumber(this.y,!0)&&0<=this.y},setVisible:function(a,c){var b=this,d=b.series,e=d.chart,f=d.options.ignoreHiddenPoint; +c=w(c,f);a!==b.visible&&(b.visible=b.options.visible=a=void 0===a?!b.visible:a,d.options.data[r(b,d.data)]=b.options,p(["graphic","dataLabel","connector","shadowGroup"],function(c){if(b[c])b[c][a?"show":"hide"](!0)}),b.legendItem&&e.legend.colorizeItem(b,a),a||"hover"!==b.state||b.setState(""),f&&(d.isDirty=!0),c&&e.redraw())},slice:function(a,c,b){var d=this.series;m(b,d.chart);w(c,!0);this.sliced=this.options.sliced=H(a)?a:!this.sliced;d.options.data[r(this,d.data)]=this.options;this.graphic.animate(this.getTranslate()); +this.shadowGroup&&this.shadowGroup.animate(this.getTranslate())},getTranslate:function(){return this.sliced?this.slicedTranslation:{translateX:0,translateY:0}},haloPath:function(a){var c=this.shapeArgs;return this.sliced||!this.visible?[]:this.series.chart.renderer.symbols.arc(c.x,c.y,c.r+a,c.r+a,{innerR:this.shapeArgs.r-1,start:c.start,end:c.end})}})})(M);(function(a){var E=a.addEvent,D=a.arrayMax,H=a.defined,p=a.each,f=a.extend,l=a.format,r=a.map,n=a.merge,w=a.noop,u=a.pick,e=a.relativeLength,h= +a.Series,m=a.seriesTypes,d=a.stableSort;a.distribute=function(a,b){function c(a,b){return a.target-b.target}var e,f=!0,h=a,l=[],m;m=0;for(e=a.length;e--;)m+=a[e].size;if(m>b){d(a,function(a,b){return(b.rank||0)-(a.rank||0)});for(m=e=0;m<=b;)m+=a[e].size,e++;l=a.splice(e-1,a.length)}d(a,c);for(a=r(a,function(a){return{size:a.size,targets:[a.target],align:u(a.align,.5)}});f;){for(e=a.length;e--;)f=a[e],m=(Math.min.apply(0,f.targets)+Math.max.apply(0,f.targets))/2,f.pos=Math.min(Math.max(0,m-f.size* +f.align),b-f.size);e=a.length;for(f=!1;e--;)0a[e].pos&&(a[e-1].size+=a[e].size,a[e-1].targets=a[e-1].targets.concat(a[e].targets),a[e-1].align=.5,a[e-1].pos+a[e-1].size>b&&(a[e-1].pos=b-a[e-1].size),a.splice(e,1),f=!0)}e=0;p(a,function(a){var b=0;p(a.targets,function(){h[e].pos=a.pos+b;b+=h[e].size;e++})});h.push.apply(h,l);d(h,c)};h.prototype.drawDataLabels=function(){function c(a,b){var c=b.filter;return c?(b=c.operator,a=a[c.property],c=c.value,"\x3e"===b&&a>c||"\x3c"=== +b&&a=c||"\x3c\x3d"===b&&a<=c||"\x3d\x3d"===b&&a==c||"\x3d\x3d\x3d"===b&&a===c?!0:!1):!0}var b=this,d=b.options,e=d.dataLabels,f=b.points,h,m,r=b.hasRendered||0,t,w,D=u(e.defer,!!d.animation),q=b.chart.renderer;if(e.enabled||b._hasPointLabels)b.dlProcessOptions&&b.dlProcessOptions(e),w=b.plotGroup("dataLabelsGroup","data-labels",D&&!r?"hidden":"visible",e.zIndex||6),D&&(w.attr({opacity:+r}),r||E(b,"afterAnimate",function(){b.visible&&w.show(!0);w[d.animation?"animate":"attr"]({opacity:1}, +{duration:200})})),m=e,p(f,function(f){var k,p=f.dataLabel,g,x,r=f.connector,z=!p,C;h=f.dlOptions||f.options&&f.options.dataLabels;(k=u(h&&h.enabled,m.enabled)&&!f.isNull)&&(k=!0===c(f,h||e));k&&(e=n(m,h),g=f.getLabelConfig(),C=e[f.formatPrefix+"Format"]||e.format,t=H(C)?l(C,g):(e[f.formatPrefix+"Formatter"]||e.formatter).call(g,e),C=e.style,g=e.rotation,C.color=u(e.color,C.color,b.color,"#000000"),"contrast"===C.color&&(f.contrastColor=q.getContrast(f.color||b.color),C.color=e.inside||0>u(f.labelDistance, +e.distance)||d.stacking?f.contrastColor:"#000000"),d.cursor&&(C.cursor=d.cursor),x={fill:e.backgroundColor,stroke:e.borderColor,"stroke-width":e.borderWidth,r:e.borderRadius||0,rotation:g,padding:e.padding,zIndex:1},a.objectEach(x,function(a,b){void 0===a&&delete x[b]}));!p||k&&H(t)?k&&H(t)&&(p?x.text=t:(p=f.dataLabel=g?q.text(t,0,-9999).addClass("highcharts-data-label"):q.label(t,0,-9999,e.shape,null,null,e.useHTML,null,"data-label"),p.addClass(" highcharts-data-label-color-"+f.colorIndex+" "+(e.className|| +"")+(e.useHTML?"highcharts-tracker":""))),p.attr(x),p.css(C).shadow(e.shadow),p.added||p.add(w),b.alignDataLabel(f,p,e,null,z)):(f.dataLabel=p=p.destroy(),r&&(f.connector=r.destroy()))})};h.prototype.alignDataLabel=function(a,b,d,e,h){var c=this.chart,k=c.inverted,l=u(a.dlBox&&a.dlBox.centerX,a.plotX,-9999),m=u(a.plotY,-9999),n=b.getBBox(),p,q=d.rotation,r=d.align,w=this.visible&&(a.series.forceDL||c.isInsidePlot(l,Math.round(m),k)||e&&c.isInsidePlot(l,k?e.x+1:e.y+e.height-1,k)),z="justify"===u(d.overflow, +"justify");if(w&&(p=d.style.fontSize,p=c.renderer.fontMetrics(p,b).b,e=f({x:k?this.yAxis.len-m:l,y:Math.round(k?this.xAxis.len-l:m),width:0,height:0},e),f(d,{width:n.width,height:n.height}),q?(z=!1,l=c.renderer.rotCorr(p,q),l={x:e.x+d.x+e.width/2+l.x,y:e.y+d.y+{top:0,middle:.5,bottom:1}[d.verticalAlign]*e.height},b[h?"attr":"animate"](l).attr({align:r}),m=(q+720)%360,m=180m,"left"===r?l.y-=m?n.height:0:"center"===r?(l.x-=n.width/2,l.y-=n.height/2):"right"===r&&(l.x-=n.width,l.y-=m?0:n.height)): +(b.align(d,null,e),l=b.alignAttr),z?a.isLabelJustified=this.justifyDataLabel(b,d,l,n,e,h):u(d.crop,!0)&&(w=c.isInsidePlot(l.x,l.y)&&c.isInsidePlot(l.x+n.width,l.y+n.height)),d.shape&&!q))b[h?"attr":"animate"]({anchorX:k?c.plotWidth-a.plotY:a.plotX,anchorY:k?c.plotHeight-a.plotX:a.plotY});w||(b.attr({y:-9999}),b.placed=!1)};h.prototype.justifyDataLabel=function(a,b,d,e,f,h){var c=this.chart,k=b.align,l=b.verticalAlign,m,n,p=a.box?0:a.padding||0;m=d.x+p;0>m&&("right"===k?b.align="left":b.x=-m,n=!0); +m=d.x+e.width-p;m>c.plotWidth&&("left"===k?b.align="right":b.x=c.plotWidth-m,n=!0);m=d.y+p;0>m&&("bottom"===l?b.verticalAlign="top":b.y=-m,n=!0);m=d.y+e.height-p;m>c.plotHeight&&("top"===l?b.verticalAlign="bottom":b.y=c.plotHeight-m,n=!0);n&&(a.placed=!h,a.align(b,null,f));return n};m.pie&&(m.pie.prototype.drawDataLabels=function(){var c=this,b=c.data,d,e=c.chart,f=c.options.dataLabels,l=u(f.connectorPadding,10),m=u(f.connectorWidth,1),n=e.plotWidth,t=e.plotHeight,r,w=c.center,q=w[2]/2,A=w[1],F,G, +g,v,E=[[],[]],L,P,J,M,y=[0,0,0,0];c.visible&&(f.enabled||c._hasPointLabels)&&(p(b,function(a){a.dataLabel&&a.visible&&a.dataLabel.shortened&&(a.dataLabel.attr({width:"auto"}).css({width:"auto",textOverflow:"clip"}),a.dataLabel.shortened=!1)}),h.prototype.drawDataLabels.apply(c),p(b,function(a){a.dataLabel&&a.visible&&(E[a.half].push(a),a.dataLabel._pos=null)}),p(E,function(b,h){var k,m,x=b.length,r=[],z;if(x)for(c.sortByAngle(b,h-.5),0d.bottom-2?k:P,h,d),F._attr={visibility:J,align:g[6]},F._pos={x:L+f.x+({left:l,right:-l}[g[6]]||0),y:P+f.y-10},g.x=L,g.y=P,u(f.crop,!0)&&(G=F.getBBox().width,k=null,L-Gn-l&&(k=Math.round(L+G-n+l),y[1]=Math.max(k,y[1])),0>P-v/2?y[0]=Math.max(Math.round(-P+v/2),y[0]):P+v/2>t&&(y[2]=Math.max(Math.round(P+v/2-t),y[2])),F.sideOverflow=k)}),0===D(y)||this.verifyDataLabelOverflow(y))&&(this.placeDataLabels(), +m&&p(this.points,function(a){var b;r=a.connector;if((F=a.dataLabel)&&F._pos&&a.visible&&0u(this.translatedThreshold,k.yAxis.len)),p=u(d.inside,!!this.options.stacking);l&&(e=n(l),0>e.y&&(e.height+=e.y,e.y=0),l=e.y+e.height-k.yAxis.len,0a+c||e+hb+d||f+lthis.pointCount))},pan:function(a,b){var c=this,d=c.hoverPoints,e;d&&r(d,function(a){a.setState()});r("xy"===b?[1,0]:[1],function(b){b= +c[b?"xAxis":"yAxis"][0];var d=b.horiz,f=a[d?"chartX":"chartY"],d=d?"mouseDownX":"mouseDownY",h=c[d],g=(b.pointRange||0)/2,k=b.getExtremes(),l=b.toValue(h-f,!0)+g,m=b.toValue(h+b.len-f,!0)-g,n=m=l(n.minWidth,0)&&this.chartHeight>=l(n.minHeight,0)}).call(this)&&f.push(a._id)};E.prototype.currentOptions=function(l){function n(e,h,l,d){var c;a.objectEach(e,function(a,e){if(!d&&-1 li.first padding-left: 0px h2 font-size: 150% - -//#subtitle -// color: lighten($brown, 30%) -// font-style: italic -// font-weight: normal -// margin-top: 0px -// padding-left: 1em -// padding-top: 0px - h3 font-size: 120% diff --git a/app/assets/stylesheets/predictions.sass b/app/assets/stylesheets/predictions.sass new file mode 100644 index 000000000..b9496ebc5 --- /dev/null +++ b/app/assets/stylesheets/predictions.sass @@ -0,0 +1,11 @@ +.predictions + .metric + height: 180px + border: #000000 2px + background: $white + margin: 4px + strong + font-size: 250% + font-align: center + h3 + diff --git a/app/controllers/crops_controller.rb b/app/controllers/crops_controller.rb index 3df1a7ca7..b62177e5d 100644 --- a/app/controllers/crops_controller.rb +++ b/app/controllers/crops_controller.rb @@ -51,11 +51,7 @@ class CropsController < ApplicationController def show @crop = Crop.includes(:scientific_names, plantings: :photos).find(params[:id]) @posts = @crop.posts.order(created_at: :desc).paginate(page: params[:page]) - - respond_to do |format| - format.html # show.html.haml - format.json { render json: @crop.to_json(crop_json_fields) } - end + respond_with(@crop) end def new @@ -106,6 +102,14 @@ class CropsController < ApplicationController respond_with @crop end + def sunniness + @crop = Crop.find(params[:crop_id]) + render json: Planting.where(crop: @crop) + .where.not(sunniness: nil) + .where.not(sunniness: '') + .group(:sunniness).count(:id) + end + private def notifier diff --git a/app/views/crops/_predictions.html.haml b/app/views/crops/_predictions.html.haml index dbde9c783..343f4f6bd 100644 --- a/app/views/crops/_predictions.html.haml +++ b/app/views/crops/_predictions.html.haml @@ -1,29 +1,34 @@ - unless crop.perennial.nil? - %p - #{crop.name} is - - if crop.perennial == true - = link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do - a perennial crop - (living more than two years) - - elsif crop.perennial == false - = link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do - an annual crop - (living and reproducing in a single year or less) + .predictions + .row + %p + #{crop.name} is + - if crop.perennial == true + = link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do + a perennial crop + (living more than two years) + - elsif crop.perennial == false + = link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do + an annual crop + (living and reproducing in a single year or less) -- if crop.annual? && crop.median_lifespan.present? - %p - Median lifespan of #{crop.name} plants is - %strong= crop.median_lifespan - days + .row + - if crop.annual? && crop.median_lifespan.present? + .metric.col-md-3.col-xs-5 + %h3 Median lifespan + %strong= crop.median_lifespan + %span days -- if crop.median_days_to_first_harvest.present? - %p - First harvest expected - %strong= crop.median_days_to_first_harvest - days after planting + - if crop.median_days_to_first_harvest.present? + .metric.col-md-3.col-xs-5 + %h3 First harvest expected + %strong= crop.median_days_to_first_harvest + %span days + after planting -- if crop.annual? && crop.median_days_to_last_harvest.present? - %p - Last harvest expected - %strong= crop.median_days_to_last_harvest - days after planting + - if crop.annual? && crop.median_days_to_last_harvest.present? + .metric.col-md-3.col-xs-5 + %h3 Last harvest expected + %strong= crop.median_days_to_last_harvest + %span days + after planting diff --git a/app/views/crops/show.html.haml b/app/views/crops/show.html.haml index 2789d61ef..cf401681e 100644 --- a/app/views/crops/show.html.haml +++ b/app/views/crops/show.html.haml @@ -8,6 +8,9 @@ = tag("meta", property: "og:url", content: request.original_url) = tag("meta", property: "og:site_name", content: ENV['GROWSTUFF_SITE_NAME']) +- content_for :scripts do + = javascript_include_tag "charts" + = render partial: 'approval_status_message', locals: { crop: @crop } - if @crop.approved? @@ -27,11 +30,6 @@ = display_seed_availability(@current_member, @crop) = link_to "View your seeds", seeds_by_owner_path(owner: current_member.slug) - %h2 Predictions - = render 'predictions', crop: @crop - - %p= render 'crops/photos', crop: @crop - %h2 - if !@crop.plantings.empty? = @crop.name.titleize @@ -41,19 +39,26 @@ - else Nobody is growing this yet. You could be the first! - %h2 - Sunniness Chart + .row + .col-md-3 + %h2 Sunniness Chart + = pie_chart crop_sunniness_path(@crop), legend: "bottom" - #sunchart + .col-md-9 + %h2 Predictions + = render 'predictions', crop: @crop - %h2 - Crop Map - %p - Only plantings by members who have set their locations are shown on this map. - - if current_member && current_member.location.blank? - = link_to "Set your location.", edit_member_registration_path + .row + .col-md-12 + %h2 Photos + %p= render 'crops/photos', crop: @crop - #cropmap + %h2 Crop Map + %p + Only plantings by members who have set their locations are shown on this map. + - if current_member && current_member.location.blank? + = link_to "Set your location.", edit_member_registration_path + #cropmap %a{ name: 'posts' } %h2 What people are saying about #{@crop.name.pluralize} diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 7fe945d8a..40f83db5b 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,5 +1,6 @@ !!! 5 %html{ lang: "en", prefix: "og: http://ogp.me/ns#" } + = yield :scripts = render partial: "layouts/meta" %body = render partial: "layouts/header" diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb new file mode 100644 index 000000000..60484d4a4 --- /dev/null +++ b/config/initializers/assets.rb @@ -0,0 +1,15 @@ +# Be sure to restart your server when you modify this file. + +# Version of your assets, change this if you want to expire all your assets. +Rails.application.config.assets.version = '1.0' + +# Add additional assets to the asset load path. +# Rails.application.config.assets.paths << Emoji.images_path +# Add Yarn node_modules folder to the asset load path. +Rails.application.config.assets.paths << Rails.root.join('node_modules') + +# Precompile additional assets. +# application.js, application.css, and all non-JS/CSS in the app/assets +# folder are already added. +# Rails.application.config.assets.precompile += %w( admin.js admin.css ) +Rails.application.config.assets.precompile += %w(charts.js) diff --git a/config/routes.rb b/config/routes.rb index eb963311a..14c4bd401 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -50,6 +50,7 @@ Growstuff::Application.routes.draw do get 'crops/search' => 'crops#search', as: 'crops_search' resources :crops do get 'photos' => 'photos#index' + get 'sunniness' => 'crops#sunniness' end resources :comments From c5699610299d84c52957bd1621fc8f425f54ba76 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 7 Jan 2018 19:25:05 +1300 Subject: [PATCH 069/219] Add timeline to gardens --- app/controllers/gardens_controller.rb | 11 +++++++++++ app/views/gardens/show.html.haml | 9 +++++++++ config/routes.rb | 4 +++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/app/controllers/gardens_controller.rb b/app/controllers/gardens_controller.rb index ede5c2602..d2aa8bfe0 100644 --- a/app/controllers/gardens_controller.rb +++ b/app/controllers/gardens_controller.rb @@ -58,6 +58,17 @@ class GardensController < ApplicationController redirect_to(gardens_by_owner_path(owner: @garden.owner)) end + def timeline + @data = [] + @garden = Garden.find(params[:garden_id]) + @garden.plantings.where.not(finished_at: nil) + .where.not(planted_at: nil) + .order(finished_at: :desc).each do |p| + @data << [p.crop.name, p.planted_at, p.finished_at] + end + render json: @data + end + private def garden_params diff --git a/app/views/gardens/show.html.haml b/app/views/gardens/show.html.haml index ef5b41cfb..5ffed7152 100644 --- a/app/views/gardens/show.html.haml +++ b/app/views/gardens/show.html.haml @@ -9,6 +9,11 @@ = tag("meta", property: "og:type", content: "website") = tag("meta", property: "og:url", content: request.original_url) = tag("meta", property: "og:site_name", content: ENV['GROWSTUFF_SITE_NAME']) + +- content_for :scripts do + = javascript_include_tag "charts" + = javascript_include_tag "https://www.gstatic.com/charts/loader.js" + .row .col-md-9 %p.btn-group= render 'gardens/actions', garden: @garden @@ -32,6 +37,10 @@ Why not = link_to 'tell us more.', edit_garden_path(@garden) + %h3 Garden timeline + .row + = timeline garden_timeline_path(@garden), adapter: "google" + %h3 What's planted here? .row - if @current_plantings.size.positive? diff --git a/config/routes.rb b/config/routes.rb index 14c4bd401..cc90a4063 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,7 +27,9 @@ Growstuff::Application.routes.draw do get '/plantings/owner/:owner' => 'plantings#index', as: 'plantings_by_owner' get '/plantings/crop/:crop' => 'plantings#index', as: 'plantings_by_crop' - resources :gardens + resources :gardens do + get 'timeline' => 'gardens#timeline' + end get '/gardens/owner/:owner' => 'gardens#index', as: 'gardens_by_owner' resources :seeds From d8cce280e2498a63e9341b12d8ed1607a1ef8df4 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 8 Jan 2018 18:03:20 +1300 Subject: [PATCH 070/219] Remove highcharts.js, we're not using it --- app/assets/javascripts/highcharts.js | 389 --------------------------- 1 file changed, 389 deletions(-) delete mode 100644 app/assets/javascripts/highcharts.js diff --git a/app/assets/javascripts/highcharts.js b/app/assets/javascripts/highcharts.js deleted file mode 100644 index 9554ef58b..000000000 --- a/app/assets/javascripts/highcharts.js +++ /dev/null @@ -1,389 +0,0 @@ -/* - Highcharts JS v6.0.4 (2017-12-15) - - (c) 2009-2016 Torstein Honsi - - License: www.highcharts.com/license -*/ -(function(S,M){"object"===typeof module&&module.exports?module.exports=S.document?M(S):M:S.Highcharts=M(S)})("undefined"!==typeof window?window:this,function(S){var M=function(){var a="undefined"===typeof S?window:S,E=a.document,D=a.navigator&&a.navigator.userAgent||"",H=E&&E.createElementNS&&!!E.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect,p=/(edge|msie|trident)/i.test(D)&&!a.opera,f=/Firefox/.test(D),l=f&&4>parseInt(D.split("Firefox/")[1],10);return a.Highcharts?a.Highcharts.error(16, -!0):{product:"Highcharts",version:"6.0.4",deg2rad:2*Math.PI/360,doc:E,hasBidiBug:l,hasTouch:E&&void 0!==E.documentElement.ontouchstart,isMS:p,isWebKit:/AppleWebKit/.test(D),isFirefox:f,isTouchDevice:/(Mobile|Android|Windows Phone)/.test(D),SVG_NS:"http://www.w3.org/2000/svg",chartCount:0,seriesTypes:{},symbolSizes:{},svg:H,win:a,marginNames:["plotTop","marginRight","marginBottom","plotLeft"],noop:function(){},charts:[]}}();(function(a){a.timers=[];var E=a.charts,D=a.doc,H=a.win;a.error=function(p, -f){p=a.isNumber(p)?"Highcharts error #"+p+": www.highcharts.com/errors/"+p:p;if(f)throw Error(p);H.console&&console.log(p)};a.Fx=function(a,f,l){this.options=f;this.elem=a;this.prop=l};a.Fx.prototype={dSetter:function(){var a=this.paths[0],f=this.paths[1],l=[],r=this.now,n=a.length,w;if(1===r)l=this.toD;else if(n===f.length&&1>r)for(;n--;)w=parseFloat(a[n]),l[n]=isNaN(w)?f[n]:r*parseFloat(f[n]-w)+w;else l=f;this.elem.attr("d",l,null,!0)},update:function(){var a=this.elem,f=this.prop,l=this.now,r= -this.options.step;if(this[f+"Setter"])this[f+"Setter"]();else a.attr?a.element&&a.attr(f,l,null,!0):a.style[f]=l+this.unit;r&&r.call(a,l,this)},run:function(p,f,l){var r=this,n=r.options,w=function(a){return w.stopped?!1:r.step(a)},u=H.requestAnimationFrame||function(a){setTimeout(a,13)},e=function(){for(var h=0;h=u+this.startTime?(this.now=this.end,this.pos=1,this.update(),l=e[this.prop]=!0,a.objectEach(e,function(a){!0!==a&&(l=!1)}),l&&w&&w.call(n),p=!1):(this.pos=r.easing((f-this.startTime)/u),this.now=this.start+(this.end- -this.start)*this.pos,this.update(),p=!0);return p},initPath:function(p,f,l){function r(a){var b,c;for(k=a.length;k--;)b="M"===a[k]||"L"===a[k],c=/[a-zA-Z]/.test(a[k+3]),b&&c&&a.splice(k+1,0,a[k+1],a[k+2],a[k+1],a[k+2])}function n(a,b){for(;a.lengtha&&-Infinityw?"AM":"PM",P:12>w?"am":"pm",S:k(n.getSeconds()),L:k(Math.round(f%1E3),3)},a.dateFormats);a.objectEach(r,function(a,b){for(;-1!==p.indexOf("%"+b);)p=p.replace("%"+b,"function"===typeof a?a(f):a)});return l?p.substr(0,1).toUpperCase()+p.substr(1):p};a.formatSingle=function(p,f){var l=/\.([0-9])/,r=a.defaultOptions.lang;/f$/.test(p)?(l=(l=p.match(l))?l[1]:-1,null!==f&&(f=a.numberFormat(f,l,r.decimalPoint,-1=l&&(f=[1/l])));for(r=0;r=p||!n&&w<=(f[r]+(f[r+1]||f[r]))/2);r++);return u=a.correctFloat(u*l,-Math.round(Math.log(.001)/Math.LN10))};a.stableSort=function(a,f){var l=a.length,p,n;for(n=0;nl&&(l=a[f]);return l};a.destroyObjectProperties=function(p,f){a.objectEach(p,function(a,r){a&&a!==f&&a.destroy&&a.destroy();delete p[r]})};a.discardElement=function(p){var f=a.garbageBin;f||(f=a.createElement("div"));p&&f.appendChild(p);f.innerHTML=""};a.correctFloat=function(a,f){return parseFloat(a.toPrecision(f||14))};a.setAnimation=function(p,f){f.renderer.globalAnimation=a.pick(p,f.options.chart.animation, -!0)};a.animObject=function(p){return a.isObject(p)?a.merge(p):{duration:p?500:0}};a.timeUnits={millisecond:1,second:1E3,minute:6E4,hour:36E5,day:864E5,week:6048E5,month:24192E5,year:314496E5};a.numberFormat=function(p,f,l,r){p=+p||0;f=+f;var n=a.defaultOptions.lang,w=(p.toString().split(".")[1]||"").split("e")[0].length,u,e,h=p.toString().split("e");-1===f?f=Math.min(w,20):a.isNumber(f)?f&&h[1]&&0>h[1]&&(u=f+ +h[1],0<=u?(h[0]=(+h[0]).toExponential(u).split("e")[0],f=u):(h[0]=h[0].split(".")[0]||0, -p=20>f?(h[0]*Math.pow(10,h[1])).toFixed(f):0,h[1]=0)):f=2;e=(Math.abs(h[1]?h[0]:p)+Math.pow(10,-Math.max(f,w)-1)).toFixed(f);w=String(a.pInt(e));u=3p?"-":"")+(u?w.substr(0,u)+r:"");p+=w.substr(u).replace(/(\d{3})(?=\d)/g,"$1"+r);f&&(p+=l+e.slice(-f));h[1]&&0!==+p&&(p+="e"+h[1]);return p};Math.easeInOutSine=function(a){return-.5*(Math.cos(Math.PI*a)-1)};a.getStyle=function(p,f,l){if("width"===f)return Math.min(p.offsetWidth, -p.scrollWidth)-a.getStyle(p,"padding-left")-a.getStyle(p,"padding-right");if("height"===f)return Math.min(p.offsetHeight,p.scrollHeight)-a.getStyle(p,"padding-top")-a.getStyle(p,"padding-bottom");H.getComputedStyle||a.error(27,!0);if(p=H.getComputedStyle(p,void 0))p=p.getPropertyValue(f),a.pick(l,"opacity"!==f)&&(p=a.pInt(p));return p};a.inArray=function(p,f){return(a.indexOfPolyfill||Array.prototype.indexOf).call(f,p)};a.grep=function(p,f){return(a.filterPolyfill||Array.prototype.filter).call(p, -f)};a.find=Array.prototype.find?function(a,f){return a.find(f)}:function(a,f){var l,r=a.length;for(l=0;l>16,(l&65280)>> -8,l&255,1]:4===f&&(n=[(l&3840)>>4|(l&3840)>>8,(l&240)>>4|l&240,(l&15)<<4|l&15,1])),!n)for(w=this.parsers.length;w--&&!n;)u=this.parsers[w],(f=u.regex.exec(l))&&(n=u.parse(f));this.rgba=n||[]},get:function(a){var f=this.input,n=this.rgba,l;this.stops?(l=p(f),l.stops=[].concat(l.stops),E(this.stops,function(n,e){l.stops[e]=[l.stops[e][0],n.get(a)]})):l=n&&D(n[0])?"rgb"===a||!a&&1===n[3]?"rgb("+n[0]+","+n[1]+","+n[2]+")":"a"===a?n[3]:"rgba("+n.join(",")+")":f;return l},brighten:function(a){var l,n=this.rgba; -if(this.stops)E(this.stops,function(n){n.brighten(a)});else if(D(a)&&0!==a)for(l=0;3>l;l++)n[l]+=f(255*a),0>n[l]&&(n[l]=0),255y.width)y={width:0,height:0}}else y=this.htmlGetBBox();b.isSVG&& -(a=y.width,b=y.height,q&&"11px"===q.fontSize&&17===Math.round(b)&&(y.height=b=14),g&&(y.width=Math.abs(b*Math.sin(v))+Math.abs(a*Math.cos(v)),y.height=Math.abs(b*Math.cos(v))+Math.abs(a*Math.sin(v))));if(A&&0]*>/g,"")))},textSetter:function(a){a!==this.textStr&&(delete this.bBox,this.textStr=a,this.added&&this.renderer.buildText(this))},fillSetter:function(a,g,b){"string"===typeof a?b.setAttribute(g,a):a&&this.colorGradient(a,g,b)},visibilitySetter:function(a,g,b){"inherit"===a?b.removeAttribute(g):this[g]!==a&&b.setAttribute(g,a);this[g]=a},zIndexSetter:function(a,b){var v=this.renderer,y=this.parentGroup,c=(y||v).element||v.box,k,d=this.element,q,e,v=c===v.box;k=this.added;var z;u(a)&& -(d.zIndex=a,a=+a,this[b]===a&&(k=!1),this[b]=a);if(k){(a=this.zIndex)&&y&&(y.handleZ=!0);b=c.childNodes;for(z=b.length-1;0<=z&&!q;z--)if(y=b[z],k=y.zIndex,e=!u(k),y!==d)if(0>a&&e&&!v&&!z)c.insertBefore(d,b[z]),q=!0;else if(g(k)<=a||e&&(!u(a)||0<=a))c.insertBefore(d,b[z+1]||null),q=!0;q||(c.insertBefore(d,b[v?3:0]||null),q=!0)}return q},_defaultSetter:function(a,g,b){b.setAttribute(g,a)}});E.prototype.yGetter=E.prototype.xGetter;E.prototype.translateXSetter=E.prototype.translateYSetter=E.prototype.rotationSetter= -E.prototype.verticalAlignSetter=E.prototype.rotationOriginXSetter=E.prototype.rotationOriginYSetter=E.prototype.scaleXSetter=E.prototype.scaleYSetter=E.prototype.matrixSetter=function(a,g){this[g]=a;this.doTransform=!0};E.prototype["stroke-widthSetter"]=E.prototype.strokeSetter=function(a,g,b){this[g]=a;this.stroke&&this["stroke-width"]?(E.prototype.fillSetter.call(this,this.stroke,"stroke",b),b.setAttribute("stroke-width",this["stroke-width"]),this.hasStroke=!0):"stroke-width"===g&&0===a&&this.hasStroke&& -(b.removeAttribute("stroke"),this.hasStroke=!1)};D=a.SVGRenderer=function(){this.init.apply(this,arguments)};c(D.prototype,{Element:E,SVG_NS:P,init:function(a,g,b,v,c,k){var y;v=this.createElement("svg").attr({version:"1.1","class":"highcharts-root"}).css(this.getStyle(v));y=v.element;a.appendChild(y);f(a,"dir","ltr");-1===a.innerHTML.indexOf("xmlns")&&f(y,"xmlns",this.SVG_NS);this.isSVG=!0;this.box=y;this.boxWrapper=v;this.alignedObjects=[];this.url=(x||N)&&m.getElementsByTagName("base").length? -R.location.href.replace(/#.*?$/,"").replace(/<[^>]*>/g,"").replace(/([\('\)])/g,"\\$1").replace(/ /g,"%20"):"";this.createElement("desc").add().element.appendChild(m.createTextNode("Created with Highcharts 6.0.4"));this.defs=this.createElement("defs").add();this.allowHTML=k;this.forExport=c;this.gradients={};this.cache={};this.cacheKeys=[];this.imgCount=0;this.setSize(g,b,!1);var d;x&&a.getBoundingClientRect&&(g=function(){n(a,{left:0,top:0});d=a.getBoundingClientRect();n(a,{left:Math.ceil(d.left)- -d.left+"px",top:Math.ceil(d.top)-d.top+"px"})},g(),this.unSubPixelFix=H(R,"resize",g))},getStyle:function(a){return this.style=c({fontFamily:'"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif',fontSize:"12px"},a)},setStyle:function(a){this.boxWrapper.css(this.getStyle(a))},isHidden:function(){return!this.boxWrapper.getBBox().width},destroy:function(){var a=this.defs;this.box=null;this.boxWrapper=this.boxWrapper.destroy();h(this.gradients||{});this.gradients=null;a&&(this.defs=a.destroy()); -this.unSubPixelFix&&this.unSubPixelFix();return this.alignedObjects=null},createElement:function(a){var g=new this.Element;g.init(this,a);return g},draw:A,getRadialAttr:function(a,g){return{cx:a[0]-a[2]/2+g.cx*a[2],cy:a[1]-a[2]/2+g.cy*a[2],r:g.r*a[2]}},getSpanWidth:function(a,g){var b=a.getBBox(!0).width;!L&&this.forExport&&(b=this.measureSpanWidth(g.firstChild.data,a.styles));return b},applyEllipsis:function(a,g,b,v){var c=a.rotation,y=b,k,d=0,q=b.length,e=function(a){g.removeChild(g.firstChild); -a&&g.appendChild(m.createTextNode(a))},z;a.rotation=0;y=this.getSpanWidth(a,g);if(z=y>v){for(;d<=q;)k=Math.ceil((d+q)/2),y=b.substring(0,k)+"\u2026",e(y),y=this.getSpanWidth(a,g),d===q?d=q+1:y>v?q=k-1:d=k;0===q&&e("")}a.rotation=c;return z},escapes:{"\x26":"\x26amp;","\x3c":"\x26lt;","\x3e":"\x26gt;","'":"\x26#39;",'"':"\x26quot;"},buildText:function(a){var b=a.element,v=this,c=v.forExport,y=G(a.textStr,"").toString(),q=-1!==y.indexOf("\x3c"),e=b.childNodes,z,h,A,J,t=f(b,"x"),x=a.styles,B=a.textWidth, -l=x&&x.lineHeight,C=x&&x.textOutline,u=x&&"ellipsis"===x.textOverflow,Q=x&&"nowrap"===x.whiteSpace,w=x&&x.fontSize,R,I,r=e.length,x=B&&!a.added&&this.box,p=function(a){var c;c=/(px|em)$/.test(a&&a.style.fontSize)?a.style.fontSize:w||v.style.fontSize||12;return l?g(l):v.fontMetrics(c,a.getAttribute("style")?a:b).h},K=function(a){F(v.escapes,function(g,b){a=a.replace(new RegExp(g,"g"),b)});return a};R=[y,u,Q,l,C,w,B].join();if(R!==a.textCache){for(a.textCache=R;r--;)b.removeChild(e[r]);q||C||u||B|| --1!==y.indexOf(" ")?(z=/<.*class="([^"]+)".*>/,h=/<.*style="([^"]+)".*>/,A=/<.*href="([^"]+)".*>/,x&&x.appendChild(b),y=q?y.replace(/<(b|strong)>/g,'\x3cspan style\x3d"font-weight:bold"\x3e').replace(/<(i|em)>/g,'\x3cspan style\x3d"font-style:italic"\x3e').replace(//g,"\x3c/span\x3e").split(//g):[y],y=k(y,function(a){return""!==a}),d(y,function(g,y){var k,q=0;g=g.replace(/^\s+|\s+$/g,"").replace(//g,"\x3c/span\x3e|||"); -k=g.split("|||");d(k,function(g){if(""!==g||1===k.length){var d={},e=m.createElementNS(v.SVG_NS,"tspan"),x,F;z.test(g)&&(x=g.match(z)[1],f(e,"class",x));h.test(g)&&(F=g.match(h)[1].replace(/(;| |^)color([ :])/,"$1fill$2"),f(e,"style",F));A.test(g)&&!c&&(f(e,"onclick",'location.href\x3d"'+g.match(A)[1]+'"'),f(e,"class","highcharts-anchor"),n(e,{cursor:"pointer"}));g=K(g.replace(/<[a-zA-Z\/](.|\n)*?>/g,"")||" ");if(" "!==g){e.appendChild(m.createTextNode(g));q?d.dx=0:y&&null!==t&&(d.x=t);f(e,d);b.appendChild(e); -!q&&I&&(!L&&c&&n(e,{display:"block"}),f(e,"dy",p(e)));if(B){d=g.replace(/([^\^])-/g,"$1- ").split(" ");x=1B,void 0===J&&(J=g),g&&1!==d.length?(e.removeChild(e.firstChild),O.unshift(d.pop())):(d=O,O=[],d.length&&!Q&&(e=m.createElementNS(P,"tspan"),f(e,{dy:C,x:t}),F&&f(e,"style",F),b.appendChild(e)),l>B&&(B=l)),d.length&&e.appendChild(m.createTextNode(d.join(" ").replace(/- /g, -"-")));a.rotation=G}q++}}});I=I||b.childNodes.length}),J&&a.attr("title",a.textStr),x&&x.removeChild(b),C&&a.applyTextOutline&&a.applyTextOutline(C)):b.appendChild(m.createTextNode(K(y)))}},getContrast:function(a){a=r(a).rgba;return 510Math.abs(c.end-c.start-2*Math.PI));var y=Math.cos(k),z=Math.sin(k),h=Math.cos(e),e=Math.sin(e);c=.001>c.end-k-Math.PI?0:1;d=["M",a+d*y,g+q*z,"A",d,q,0,c,1,a+d*h,g+q*e];u(b)&&d.push(v?"M":"L",a+b* -h,g+b*e,"A",b,b,0,c,0,a+b*y,g+b*z);d.push(v?"":"Z");return d},callout:function(a,g,b,v,c){var d=Math.min(c&&c.r||0,b,v),k=d+6,q=c&&c.anchorX;c=c&&c.anchorY;var e;e=["M",a+d,g,"L",a+b-d,g,"C",a+b,g,a+b,g,a+b,g+d,"L",a+b,g+v-d,"C",a+b,g+v,a+b,g+v,a+b-d,g+v,"L",a+d,g+v,"C",a,g+v,a,g+v,a,g+v-d,"L",a,g+d,"C",a,g,a,g,a+d,g];q&&q>b?c>g+k&&cq?c>g+k&&cv&&q>a+k&&qc&&q>a+k&&qa?a+3:Math.round(1.2*a);return{h:b,b:Math.round(.8* -b),f:a}},rotCorr:function(a,g,b){var v=a;g&&b&&(v=Math.max(v*Math.cos(g*e),4));return{x:-a/3*Math.sin(g*e),y:v}},label:function(g,b,k,e,z,h,m,A,L){var y=this,J=y.g("button"!==L&&"label"),t=J.text=y.text("",0,0,m).attr({zIndex:1}),x,F,n=0,B=3,l=0,C,f,Q,G,w,R={},I,P,r=/^url\((.*?)\)$/.test(e),p=r,K,O,N,T;L&&J.addClass("highcharts-"+L);p=r;K=function(){return(I||0)%2/2};O=function(){var a=t.element.style,g={};F=(void 0===C||void 0===f||w)&&u(t.textStr)&&t.getBBox();J.width=(C||F.width||0)+2*B+l;J.height= -(f||F.height||0)+2*B;P=B+y.fontMetrics(a&&a.fontSize,t).b;p&&(x||(J.box=x=y.symbols[e]||r?y.symbol(e):y.rect(),x.addClass(("button"===L?"":"highcharts-label-box")+(L?" highcharts-"+L+"-box":"")),x.add(J),a=K(),g.x=a,g.y=(A?-P:0)+a),g.width=Math.round(J.width),g.height=Math.round(J.height),x.attr(c(g,R)),R={})};N=function(){var a=l+B,g;g=A?0:P;u(C)&&F&&("center"===w||"right"===w)&&(a+={center:.5,right:1}[w]*(C-F.width));if(a!==t.x||g!==t.y)t.attr("x",a),void 0!==g&&t.attr("y",g);t.x=a;t.y=g};T=function(a, -g){x?x.attr(a,g):R[a]=g};J.onAdd=function(){t.add(J);J.attr({text:g||0===g?g:"",x:b,y:k});x&&u(z)&&J.attr({anchorX:z,anchorY:h})};J.widthSetter=function(g){C=a.isNumber(g)?g:null};J.heightSetter=function(a){f=a};J["text-alignSetter"]=function(a){w=a};J.paddingSetter=function(a){u(a)&&a!==B&&(B=J.padding=a,N())};J.paddingLeftSetter=function(a){u(a)&&a!==l&&(l=a,N())};J.alignSetter=function(a){a={left:0,center:.5,right:1}[a];a!==n&&(n=a,F&&J.attr({x:Q}))};J.textSetter=function(a){void 0!==a&&t.textSetter(a); -O();N()};J["stroke-widthSetter"]=function(a,g){a&&(p=!0);I=this["stroke-width"]=a;T(g,a)};J.strokeSetter=J.fillSetter=J.rSetter=function(a,g){"r"!==g&&("fill"===g&&a&&(p=!0),J[g]=a);T(g,a)};J.anchorXSetter=function(a,g){z=J.anchorX=a;T(g,Math.round(a)-K()-Q)};J.anchorYSetter=function(a,g){h=J.anchorY=a;T(g,a-G)};J.xSetter=function(a){J.x=a;n&&(a-=n*((C||F.width)+2*B));Q=Math.round(a);J.attr("translateX",Q)};J.ySetter=function(a){G=J.y=Math.round(a);J.attr("translateY",G)};var U=J.css;return c(J,{css:function(a){if(a){var g= -{};a=q(a);d(J.textProps,function(b){void 0!==a[b]&&(g[b]=a[b],delete a[b])});t.css(g)}return U.call(J,a)},getBBox:function(){return{width:F.width+2*B,height:F.height+2*B,x:F.x-B,y:F.y-B}},shadow:function(a){a&&(O(),x&&x.shadow(a));return J},destroy:function(){v(J.element,"mouseenter");v(J.element,"mouseleave");t&&(t=t.destroy());x&&(x=x.destroy());E.prototype.destroy.call(J);J=y=O=N=T=null}})}});a.Renderer=D})(M);(function(a){var E=a.attr,D=a.createElement,H=a.css,p=a.defined,f=a.each,l=a.extend, -r=a.isFirefox,n=a.isMS,w=a.isWebKit,u=a.pick,e=a.pInt,h=a.SVGRenderer,m=a.win,d=a.wrap;l(a.SVGElement.prototype,{htmlCss:function(a){var b=this.element;if(b=a&&"SPAN"===b.tagName&&a.width)delete a.width,this.textWidth=b,this.updateTransform();a&&"ellipsis"===a.textOverflow&&(a.whiteSpace="nowrap",a.overflow="hidden");this.styles=l(this.styles,a);H(this.element,a);return this},htmlGetBBox:function(){var a=this.element;return{x:a.offsetLeft,y:a.offsetTop,width:a.offsetWidth,height:a.offsetHeight}}, -htmlUpdateTransform:function(){if(this.added){var a=this.renderer,b=this.element,d=this.translateX||0,z=this.translateY||0,h=this.x||0,m=this.y||0,x=this.textAlign||"left",n={left:0,center:.5,right:1}[x],t=this.styles;H(b,{marginLeft:d,marginTop:z});this.shadows&&f(this.shadows,function(a){H(a,{marginLeft:d+1,marginTop:z+1})});this.inverted&&f(b.childNodes,function(c){a.invertChild(c,b)});if("SPAN"===b.tagName){var l=this.rotation,u=e(this.textWidth),q=t&&t.whiteSpace,A=[l,x,b.innerHTML,this.textWidth, -this.textAlign].join();A!==this.cTT&&(t=a.fontMetrics(b.style.fontSize).b,p(l)&&this.setSpanRotation(l,n,t),H(b,{width:"",whiteSpace:q||"nowrap"}),b.offsetWidth>u&&/[ \-]/.test(b.textContent||b.innerText)&&H(b,{width:u+"px",display:"block",whiteSpace:q||"normal"}),this.getSpanCorrection(b.offsetWidth,t,n,l,x));H(b,{left:h+(this.xCorr||0)+"px",top:m+(this.yCorr||0)+"px"});w&&(t=b.offsetHeight);this.cTT=A}}else this.alignOnAdd=!0},setSpanRotation:function(a,b,d){var c={},k=this.renderer.getTransformKey(); -c[k]=c.transform="rotate("+a+"deg)";c[k+(r?"Origin":"-origin")]=c.transformOrigin=100*b+"% "+d+"px";H(this.element,c)},getSpanCorrection:function(a,b,d){this.xCorr=-a*d;this.yCorr=-b}});l(h.prototype,{getTransformKey:function(){return n&&!/Edge/.test(m.navigator.userAgent)?"-ms-transform":w?"-webkit-transform":r?"MozTransform":m.opera?"-o-transform":""},html:function(a,b,k){var c=this.createElement("span"),e=c.element,h=c.renderer,m=h.isSVG,w=function(a,b){f(["opacity","visibility"],function(c){d(a, -c+"Setter",function(a,c,d,k){a.call(this,c,d,k);b[d]=c})})};c.textSetter=function(a){a!==e.innerHTML&&delete this.bBox;this.textStr=a;e.innerHTML=u(a,"");c.htmlUpdateTransform()};m&&w(c,c.element.style);c.xSetter=c.ySetter=c.alignSetter=c.rotationSetter=function(a,b){"align"===b&&(b="textAlign");c[b]=a;c.htmlUpdateTransform()};c.attr({text:a,x:Math.round(b),y:Math.round(k)}).css({fontFamily:this.style.fontFamily,fontSize:this.style.fontSize,position:"absolute"});e.style.whiteSpace="nowrap";c.css= -c.htmlCss;m&&(c.add=function(a){var b,d=h.box.parentNode,k=[];if(this.parentGroup=a){if(b=a.div,!b){for(;a;)k.push(a),a=a.parentGroup;f(k.reverse(),function(a){function e(g,b){a[b]=g;n?q[h.getTransformKey()]="translate("+(a.x||a.translateX)+"px,"+(a.y||a.translateY)+"px)":"translateX"===b?q.left=g+"px":q.top=g+"px";a.doTransform=!0}var q,g=E(a.element,"class");g&&(g={className:g});b=a.div=a.div||D("div",g,{position:"absolute",left:(a.translateX||0)+"px",top:(a.translateY||0)+"px",display:a.display, -opacity:a.opacity,pointerEvents:a.styles&&a.styles.pointerEvents},b||d);q=b.style;l(a,{classSetter:function(a){return function(g){this.element.setAttribute("class",g);a.className=g}}(b),on:function(){k[0].div&&c.on.apply({element:k[0].div},arguments);return a},translateXSetter:e,translateYSetter:e});w(a,q)})}}else b=d;b.appendChild(e);c.added=!0;c.alignOnAdd&&c.htmlUpdateTransform();return c});return c}})})(M);(function(a){function E(){var n=a.defaultOptions.global,l=r.moment;if(n.timezone){if(l)return function(a){return-l.tz(a, -n.timezone).utcOffset()};a.error(25)}return n.useUTC&&n.getTimezoneOffset}function D(){var n=a.defaultOptions.global,f,u=n.useUTC,e=u?"getUTC":"get",h=u?"setUTC":"set",m="Minutes Hours Day Date Month FullYear".split(" "),d=m.concat(["Milliseconds","Seconds"]);a.Date=f=n.Date||r.Date;f.hcTimezoneOffset=u&&n.timezoneOffset;f.hcGetTimezoneOffset=E();f.hcHasTimeZone=!(!f.hcTimezoneOffset&&!f.hcGetTimezoneOffset);f.hcMakeTime=function(a,b,d,e,h,m){var c;u?(c=f.UTC.apply(0,arguments),c+=p(c)):c=(new f(a, -b,l(d,1),l(e,0),l(h,0),l(m,0))).getTime();return c};for(n=0;nb&&e-k*zm&&(p=Math.round((h-e)/Math.cos(b*r)));else if(h=e+(1-k)*z,e-k*zm&&(I=m-a.x+I*k,x=-1),I=Math.min(B,I),II||f.autoRotation&&(c.styles||{}).width)p=I;p&&(t.width=p,(n.style||{}).textOverflow||(t.textOverflow="ellipsis"),c.css(t))},getPosition:function(a,f,l,e){var h=this.axis,m=h.chart,d=e&&m.oldChartHeight||m.chartHeight;return{x:a?h.translate(f+l,null,null,e)+h.transB:h.left+h.offset+(h.opposite?(e&&m.oldChartWidth||m.chartWidth)-h.right-h.left:0),y:a?d-h.bottom+h.offset-(h.opposite?h.height:0):d-h.translate(f+l,null,null,e)-h.transB}},getLabelPosition:function(a, -f,l,e,h,m,d,c){var b=this.axis,k=b.transA,z=b.reversed,B=b.staggerLines,n=b.tickRotCorr||{x:0,y:0},x=h.y,u=e||b.reserveSpaceDefault?0:-b.labelOffset*("center"===b.labelAlign?.5:1);D(x)||(x=0===b.side?l.rotation?-8:-l.getBBox().height:2===b.side?n.y+8:Math.cos(l.rotation*r)*(n.y-l.getBBox(!1,0).height/2));a=a+h.x+u+n.x-(m&&e?m*k*(z?-1:1):0);f=f+x-(m&&!e?m*k*(z?1:-1):0);B&&(l=d/(c||1)%B,b.opposite&&(l=B-l-1),f+=b.labelOffset/B*l);return{x:a,y:Math.round(f)}},getMarkPath:function(a,f,l,e,h,m){return m.crispLine(["M", -a,f,"L",a+(h?0:-l),f+(h?l:0)],e)},renderGridLine:function(a,f,l){var e=this.axis,h=e.options,m=this.gridLine,d={},c=this.pos,b=this.type,k=e.tickmarkOffset,z=e.chart.renderer,B=b?b+"Grid":"grid",n=h[B+"LineWidth"],x=h[B+"LineColor"],h=h[B+"LineDashStyle"];m||(d.stroke=x,d["stroke-width"]=n,h&&(d.dashstyle=h),b||(d.zIndex=1),a&&(d.opacity=0),this.gridLine=m=z.path().attr(d).addClass("highcharts-"+(b?b+"-":"")+"grid-line").add(e.gridGroup));if(!a&&m&&(a=e.getPlotLinePath(c+k,m.strokeWidth()*l,a,!0)))m[this.isNew? -"attr":"animate"]({d:a,opacity:f})},renderMark:function(a,f,u){var e=this.axis,h=e.options,m=e.chart.renderer,d=this.type,c=d?d+"Tick":"tick",b=e.tickSize(c),k=this.mark,z=!k,B=a.x;a=a.y;var n=l(h[c+"Width"],!d&&e.isXAxis?1:0),h=h[c+"Color"];b&&(e.opposite&&(b[0]=-b[0]),z&&(this.mark=k=m.path().addClass("highcharts-"+(d?d+"-":"")+"tick").add(e.axisGroup),k.attr({stroke:h,"stroke-width":n})),k[z?"attr":"animate"]({d:this.getMarkPath(B,a,b[0],k.strokeWidth()*u,e.horiz,m),opacity:f}))},renderLabel:function(a, -f,u,e){var h=this.axis,m=h.horiz,d=h.options,c=this.label,b=d.labels,k=b.step,h=h.tickmarkOffset,z=!0,B=a.x;a=a.y;c&&p(B)&&(c.xy=a=this.getLabelPosition(B,a,c,m,b,h,e,k),this.isFirst&&!this.isLast&&!l(d.showFirstLabel,1)||this.isLast&&!this.isFirst&&!l(d.showLastLabel,1)?z=!1:!m||b.step||b.rotation||f||0===u||this.handleOverflow(a),k&&e%k&&(z=!1),z&&p(a.y)?(a.opacity=u,c[this.isNewLabel?"attr":"animate"](a),this.isNewLabel=!1):(c.attr("y",-9999),this.isNewLabel=!0))},render:function(a,f,u){var e= -this.axis,h=e.horiz,m=this.getPosition(h,this.pos,e.tickmarkOffset,f),d=m.x,c=m.y,e=h&&d===e.pos+e.len||!h&&c===e.pos?-1:1;u=l(u,1);this.isActive=!0;this.renderGridLine(f,u,e);this.renderMark(m,u,e);this.renderLabel(m,f,u,a);this.isNew=!1},destroy:function(){H(this,this.axis)}}})(M);var V=function(a){var E=a.addEvent,D=a.animObject,H=a.arrayMax,p=a.arrayMin,f=a.color,l=a.correctFloat,r=a.defaultOptions,n=a.defined,w=a.deg2rad,u=a.destroyObjectProperties,e=a.each,h=a.extend,m=a.fireEvent,d=a.format, -c=a.getMagnitude,b=a.grep,k=a.inArray,z=a.isArray,B=a.isNumber,I=a.isString,x=a.merge,K=a.normalizeTickInterval,t=a.objectEach,C=a.pick,N=a.removeEvent,q=a.splat,A=a.syncTimeout,F=a.Tick,G=function(){this.init.apply(this,arguments)};a.extend(G.prototype,{defaultOptions:{dateTimeLabelFormats:{millisecond:"%H:%M:%S.%L",second:"%H:%M:%S",minute:"%H:%M",hour:"%H:%M",day:"%e. %b",week:"%e. %b",month:"%b '%y",year:"%Y"},endOnTick:!1,labels:{enabled:!0,style:{color:"#666666",cursor:"default",fontSize:"11px"}, -x:0},maxPadding:.01,minorTickLength:2,minorTickPosition:"outside",minPadding:.01,startOfWeek:1,startOnTick:!1,tickLength:10,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",title:{align:"middle",style:{color:"#666666"}},type:"linear",minorGridLineColor:"#f2f2f2",minorGridLineWidth:1,minorTickColor:"#999999",lineColor:"#ccd6eb",lineWidth:1,gridLineColor:"#e6e6e6",tickColor:"#ccd6eb"},defaultYAxisOptions:{endOnTick:!0,tickPixelInterval:72,showLastLabel:!0,labels:{x:-8},maxPadding:.05, -minPadding:.05,startOnTick:!0,title:{rotation:270,text:"Values"},stackLabels:{allowOverlap:!1,enabled:!1,formatter:function(){return a.numberFormat(this.total,-1)},style:{fontSize:"11px",fontWeight:"bold",color:"#000000",textOutline:"1px contrast"}},gridLineWidth:1,lineWidth:0},defaultLeftAxisOptions:{labels:{x:-15},title:{rotation:270}},defaultRightAxisOptions:{labels:{x:15},title:{rotation:90}},defaultBottomAxisOptions:{labels:{autoRotation:[-45],x:0},title:{rotation:0}},defaultTopAxisOptions:{labels:{autoRotation:[-45], -x:0},title:{rotation:0}},init:function(a,b){var g=b.isX,v=this;v.chart=a;v.horiz=a.inverted&&!v.isZAxis?!g:g;v.isXAxis=g;v.coll=v.coll||(g?"xAxis":"yAxis");v.opposite=b.opposite;v.side=b.side||(v.horiz?v.opposite?0:2:v.opposite?1:3);v.setOptions(b);var c=this.options,d=c.type;v.labelFormatter=c.labels.formatter||v.defaultLabelFormatter;v.userOptions=b;v.minPixelPadding=0;v.reversed=c.reversed;v.visible=!1!==c.visible;v.zoomEnabled=!1!==c.zoomEnabled;v.hasNames="category"===d||!0===c.categories;v.categories= -c.categories||v.hasNames;v.names=v.names||[];v.plotLinesAndBandsGroups={};v.isLog="logarithmic"===d;v.isDatetimeAxis="datetime"===d;v.positiveValuesOnly=v.isLog&&!v.allowNegativeLog;v.isLinked=n(c.linkedTo);v.ticks={};v.labelEdge=[];v.minorTicks={};v.plotLinesAndBands=[];v.alternateBands={};v.len=0;v.minRange=v.userMinRange=c.minRange||c.maxZoom;v.range=c.range;v.offset=c.offset||0;v.stacks={};v.oldStacks={};v.stacksTouched=0;v.max=null;v.min=null;v.crosshair=C(c.crosshair,q(a.options.tooltip.crosshairs)[g? -0:1],!1);b=v.options.events;-1===k(v,a.axes)&&(g?a.axes.splice(a.xAxis.length,0,v):a.axes.push(v),a[v.coll].push(v));v.series=v.series||[];a.inverted&&!v.isZAxis&&g&&void 0===v.reversed&&(v.reversed=!0);t(b,function(a,g){E(v,g,a)});v.lin2log=c.linearToLogConverter||v.lin2log;v.isLog&&(v.val2lin=v.log2lin,v.lin2val=v.lin2log)},setOptions:function(a){this.options=x(this.defaultOptions,"yAxis"===this.coll&&this.defaultYAxisOptions,[this.defaultTopAxisOptions,this.defaultRightAxisOptions,this.defaultBottomAxisOptions, -this.defaultLeftAxisOptions][this.side],x(r[this.coll],a))},defaultLabelFormatter:function(){var g=this.axis,b=this.value,c=g.categories,k=this.dateTimeLabelFormat,e=r.lang,q=e.numericSymbols,e=e.numericSymbolMagnitude||1E3,h=q&&q.length,m,z=g.options.labels.format,g=g.isLog?Math.abs(b):g.tickInterval;if(z)m=d(z,this);else if(c)m=b;else if(k)m=a.dateFormat(k,b);else if(h&&1E3<=g)for(;h--&&void 0===m;)c=Math.pow(e,h+1),g>=c&&0===10*b%c&&null!==q[h]&&0!==b&&(m=a.numberFormat(b/c,-1)+q[h]);void 0=== -m&&(m=1E4<=Math.abs(b)?a.numberFormat(b,-1):a.numberFormat(b,-1,void 0,""));return m},getSeriesExtremes:function(){var a=this,v=a.chart;a.hasVisibleSeries=!1;a.dataMin=a.dataMax=a.threshold=null;a.softThreshold=!a.isXAxis;a.buildStacks&&a.buildStacks();e(a.series,function(g){if(g.visible||!v.options.chart.ignoreHiddenSeries){var c=g.options,d=c.threshold,k;a.hasVisibleSeries=!0;a.positiveValuesOnly&&0>=d&&(d=null);if(a.isXAxis)c=g.xData,c.length&&(g=p(c),k=H(c),B(g)||g instanceof Date||(c=b(c,B), -g=p(c)),a.dataMin=Math.min(C(a.dataMin,c[0],g),g),a.dataMax=Math.max(C(a.dataMax,c[0],k),k));else if(g.getExtremes(),k=g.dataMax,g=g.dataMin,n(g)&&n(k)&&(a.dataMin=Math.min(C(a.dataMin,g),g),a.dataMax=Math.max(C(a.dataMax,k),k)),n(d)&&(a.threshold=d),!c.softThreshold||a.positiveValuesOnly)a.softThreshold=!1}})},translate:function(a,b,c,d,k,e){var g=this.linkedParent||this,v=1,q=0,h=d?g.oldTransA:g.transA;d=d?g.oldMin:g.min;var m=g.minPixelPadding;k=(g.isOrdinal||g.isBroken||g.isLog&&k)&&g.lin2val; -h||(h=g.transA);c&&(v*=-1,q=g.len);g.reversed&&(v*=-1,q-=v*(g.sector||g.len));b?(a=(a*v+q-m)/h+d,k&&(a=g.lin2val(a))):(k&&(a=g.val2lin(a)),a=B(d)?v*(a-d)*h+q+v*m+(B(e)?h*e:0):void 0);return a},toPixels:function(a,b){return this.translate(a,!1,!this.horiz,null,!0)+(b?0:this.pos)},toValue:function(a,b){return this.translate(a-(b?0:this.pos),!0,!this.horiz,null,!0)},getPlotLinePath:function(a,b,c,d,k){var g=this.chart,v=this.left,q=this.top,e,h,m=c&&g.oldChartHeight||g.chartHeight,z=c&&g.oldChartWidth|| -g.chartWidth,A;e=this.transB;var t=function(a,g,b){if(ab)d?a=Math.min(Math.max(g,a),b):A=!0;return a};k=C(k,this.translate(a,null,null,c));a=c=Math.round(k+e);e=h=Math.round(m-k-e);B(k)?this.horiz?(e=q,h=m-this.bottom,a=c=t(a,v,v+this.width)):(a=v,c=z-this.right,e=h=t(e,q,q+this.height)):(A=!0,d=!1);return A&&!d?null:g.renderer.crispLine(["M",a,e,"L",c,h],b||1)},getLinearTickPositions:function(a,b,c){var g,v=l(Math.floor(b/a)*a);c=l(Math.ceil(c/a)*a);var d=[],k;l(v+a)===v&&(k=20);if(this.single)return[b]; -for(b=v;b<=c;){d.push(b);b=l(b+a,k);if(b===g)break;g=b}return d},getMinorTickInterval:function(){var a=this.options;return!0===a.minorTicks?C(a.minorTickInterval,"auto"):!1===a.minorTicks?null:a.minorTickInterval},getMinorTickPositions:function(){var a=this,b=a.options,c=a.tickPositions,d=a.minorTickInterval,k=[],q=a.pointRangePadding||0,h=a.min-q,q=a.max+q,m=q-h;if(m&&m/d=this.minRange,t=this.minRange,d=(t-c+b)/2,d=[b-d,C(a.min,b-d)],k&&(d[2]=this.isLog?this.log2lin(this.dataMin):this.dataMin),b=H(d),c=[b+t,C(a.max,b+t)],k&&(c[2]=this.isLog?this.log2lin(this.dataMax):this.dataMax),c=p(c),c-b=p?(r=p,f=0):b.dataMax<=p&&(w=p,x=0)),b.min= -C(N,r,b.dataMin),b.max=C(D,w,b.dataMax));q&&(b.positiveValuesOnly&&!g&&0>=Math.min(b.min,C(b.dataMin,b.min))&&a.error(10,1),b.min=l(h(b.min),15),b.max=l(h(b.max),15));b.range&&n(b.max)&&(b.userMin=b.min=N=Math.max(b.dataMin,b.minFromRange()),b.userMax=D=b.max,b.range=null);m(b,"foundExtremes");b.beforePadding&&b.beforePadding();b.adjustForMinRange();!(G||b.axisPointRange||b.usePercentage||t)&&n(b.min)&&n(b.max)&&(h=b.max-b.min)&&(!n(N)&&f&&(b.min-=h*f),!n(D)&&x&&(b.max+=h*x));B(k.softMin)&&!B(b.userMin)&& -(b.min=Math.min(b.min,k.softMin));B(k.softMax)&&!B(b.userMax)&&(b.max=Math.max(b.max,k.softMax));B(k.floor)&&(b.min=Math.max(b.min,k.floor));B(k.ceiling)&&(b.max=Math.min(b.max,k.ceiling));I&&n(b.dataMin)&&(p=p||0,!n(N)&&b.min=p?b.min=p:!n(D)&&b.max>p&&b.dataMax<=p&&(b.max=p));b.tickInterval=b.min===b.max||void 0===b.min||void 0===b.max?1:t&&!F&&u===b.linkedParent.options.tickPixelInterval?F=b.linkedParent.tickInterval:C(F,this.tickAmount?(b.max-b.min)/Math.max(this.tickAmount-1,1): -void 0,G?1:(b.max-b.min)*u/Math.max(b.len,u));A&&!g&&e(b.series,function(a){a.processData(b.min!==b.oldMin||b.max!==b.oldMax)});b.setAxisTranslation(!0);b.beforeSetTickPositions&&b.beforeSetTickPositions();b.postProcessTickInterval&&(b.tickInterval=b.postProcessTickInterval(b.tickInterval));b.pointRange&&!F&&(b.tickInterval=Math.max(b.pointRange,b.tickInterval));g=C(k.minTickInterval,b.isDatetimeAxis&&b.closestPointRange);!F&&b.tickIntervalb.tickInterval&&1E3b.max)),!!this.tickAmount));this.tickAmount||(b.tickInterval=b.unsquish());this.setTickPositions()},setTickPositions:function(){var a=this.options,b,c=a.tickPositions;b=this.getMinorTickInterval();var d=a.tickPositioner,k=a.startOnTick,q=a.endOnTick;this.tickmarkOffset=this.categories&&"between"===a.tickmarkPlacement&&1===this.tickInterval?.5:0;this.minorTickInterval="auto"===b&&this.tickInterval?this.tickInterval/ -5:b;this.single=this.min===this.max&&n(this.min)&&!this.tickAmount&&(parseInt(this.min,10)===this.min||!1!==a.allowDecimals);this.tickPositions=b=c&&c.slice();!b&&(b=this.isDatetimeAxis?this.getTimeTicks(this.normalizeTimeTickInterval(this.tickInterval,a.units),this.min,this.max,a.startOfWeek,this.ordinalPositions,this.closestPointRange,!0):this.isLog?this.getLogTickPositions(this.tickInterval,this.min,this.max):this.getLinearTickPositions(this.tickInterval,this.min,this.max),b.length>this.len&&(b= -[b[0],b.pop()],b[0]===b[1]&&(b.length=1)),this.tickPositions=b,d&&(d=d.apply(this,[this.min,this.max])))&&(this.tickPositions=b=d);this.paddedTicks=b.slice(0);this.trimTicks(b,k,q);this.isLinked||(this.single&&2>b.length&&(this.min-=.5,this.max+=.5),c||d||this.adjustTickAmount())},trimTicks:function(a,b,c){var g=a[0],d=a[a.length-1],k=this.minPointOffset||0;if(!this.isLinked){if(b&&-Infinity!==g)this.min=g;else for(;this.min-k>a[0];)a.shift();if(c)this.max=d;else for(;this.max+kb&&(this.finalTickAmt=b,b=5);this.tickAmount=b},adjustTickAmount:function(){var a=this.tickInterval,b=this.tickPositions,c=this.tickAmount,d=this.finalTickAmt,k=b&&b.length,q=C(this.threshold,this.softThreshold?0:null);if(this.hasData()){if(kc&&(this.tickInterval*= -2,this.setTickPositions());if(n(d)){for(a=c=b.length;a--;)(3===d&&1===a%2||2>=d&&0d&&(a=d)),n(c)&&(bd&&(b=d))),this.displayBtn=void 0!==a||void 0!==b,this.setExtremes(a,b,!1,void 0,{trigger:"zoom"});return!0},setAxisSize:function(){var b=this.chart,c=this.options,d=c.offsets||[0,0,0,0],k=this.horiz,q=this.width=Math.round(a.relativeLength(C(c.width,b.plotWidth-d[3]+d[1]),b.plotWidth)),e=this.height=Math.round(a.relativeLength(C(c.height, -b.plotHeight-d[0]+d[2]),b.plotHeight)),h=this.top=Math.round(a.relativeLength(C(c.top,b.plotTop+d[0]),b.plotHeight,b.plotTop)),c=this.left=Math.round(a.relativeLength(C(c.left,b.plotLeft+d[3]),b.plotWidth,b.plotLeft));this.bottom=b.chartHeight-e-h;this.right=b.chartWidth-q-c;this.len=Math.max(k?q:e,0);this.pos=k?c:h},getExtremes:function(){var a=this.isLog,b=this.lin2log;return{min:a?l(b(this.min)):this.min,max:a?l(b(this.max)):this.max,dataMin:this.dataMin,dataMax:this.dataMax,userMin:this.userMin, -userMax:this.userMax}},getThreshold:function(a){var b=this.isLog,g=this.lin2log,c=b?g(this.min):this.min,b=b?g(this.max):this.max;null===a?a=c:c>a?a=c:ba?"right":195a?"left":"center"},tickSize:function(a){var b=this.options,g=b[a+"Length"],c=C(b[a+"Width"],"tick"===a&&this.isXAxis?1:0);if(c&&g)return"inside"===b[a+"Position"]&&(g=-g),[g,c]},labelMetrics:function(){var a= -this.tickPositions&&this.tickPositions[0]||0;return this.chart.renderer.fontMetrics(this.options.labels.style&&this.options.labels.style.fontSize,this.ticks[a]&&this.ticks[a].label)},unsquish:function(){var a=this.options.labels,b=this.horiz,c=this.tickInterval,d=c,k=this.len/(((this.categories?1:0)+this.max-this.min)/c),q,h=a.rotation,m=this.labelMetrics(),z,A=Number.MAX_VALUE,t,x=function(a){a/=k||1;a=1=a)z=x(Math.abs(m.h/Math.sin(w*a))),b=z+Math.abs(a/360),b(c.step||0)&&!c.rotation&&(this.staggerLines||1)*this.len/d||!b&&(c.style&&parseInt(c.style.width,10)||k&&k-a.spacing[3]|| -.33*a.chartWidth)},renderUnsquish:function(){var a=this.chart,b=a.renderer,c=this.tickPositions,d=this.ticks,k=this.options.labels,q=this.horiz,h=this.getSlotWidth(),m=Math.max(1,Math.round(h-2*(k.padding||5))),z={},A=this.labelMetrics(),t=k.style&&k.style.textOverflow,f,F=0,l,B;I(k.rotation)||(z.rotation=k.rotation||0);e(c,function(a){(a=d[a])&&a.labelLength>F&&(F=a.labelLength)});this.maxLabelLength=F;if(this.autoRotation)F>m&&F>A.h?z.rotation=this.labelRotation:this.labelRotation=0;else if(h&& -(f={width:m+"px"},!t))for(f.textOverflow="clip",l=c.length;!q&&l--;)if(B=c[l],m=d[B].label)m.styles&&"ellipsis"===m.styles.textOverflow?m.css({textOverflow:"clip"}):d[B].labelLength>h&&m.css({width:h+"px"}),m.getBBox().height>this.len/c.length-(A.h-A.f)&&(m.specCss={textOverflow:"ellipsis"});z.rotation&&(f={width:(F>.5*a.chartHeight?.33*a.chartHeight:a.chartHeight)+"px"},t||(f.textOverflow="ellipsis"));if(this.labelAlign=k.align||this.autoLabelAlign(this.labelRotation))z.align=this.labelAlign;e(c, -function(a){var b=(a=d[a])&&a.label;b&&(b.attr(z),f&&b.css(x(f,b.specCss)),delete b.specCss,a.rotation=z.rotation)});this.tickRotCorr=b.rotCorr(A.b,this.labelRotation||0,0!==this.side)},hasData:function(){return this.hasVisibleSeries||n(this.min)&&n(this.max)&&this.tickPositions&&0=this.min&&a<=this.max)g[a]||(g[a]=new F(this,a)),d&&g[a].isNew&&g[a].render(b,!0,.1),g[a].render(b)},render:function(){var b= -this,c=b.chart,d=b.options,k=b.isLog,q=b.lin2log,h=b.isLinked,m=b.tickPositions,z=b.axisTitle,x=b.ticks,f=b.minorTicks,l=b.alternateBands,C=d.stackLabels,n=d.alternateGridColor,u=b.tickmarkOffset,G=b.axisLine,p=b.showAxis,I=D(c.renderer.globalAnimation),r,w;b.labelEdge.length=0;b.overlap=!1;e([x,f,l],function(a){t(a,function(a){a.isActive=!1})});if(b.hasData()||h)b.minorTickInterval&&!b.categories&&e(b.getMinorTickPositions(),function(a){b.renderMinorTick(a)}),m.length&&(e(m,function(a,c){b.renderTick(a, -c)}),u&&(0===b.min||b.single)&&(x[-1]||(x[-1]=new F(b,-1,null,!0)),x[-1].render(-1))),n&&e(m,function(d,g){w=void 0!==m[g+1]?m[g+1]+u:b.max-u;0===g%2&&d=h.second?0:C*Math.floor(x.getMilliseconds()/C));if(t>=h.second)x[D.hcSetSeconds](t>=h.minute? -0:C*Math.floor(x.getSeconds()/C));if(t>=h.minute)x[D.hcSetMinutes](t>=h.hour?0:C*Math.floor(x[D.hcGetMinutes]()/C));if(t>=h.hour)x[D.hcSetHours](t>=h.day?0:C*Math.floor(x[D.hcGetHours]()/C));if(t>=h.day)x[D.hcSetDate](t>=h.month?1:C*Math.floor(x[D.hcGetDate]()/C));t>=h.month&&(x[D.hcSetMonth](t>=h.year?0:C*Math.floor(x[D.hcGetMonth]()/C)),n=x[D.hcGetFullYear]());if(t>=h.year)x[D.hcSetFullYear](n-n%C);if(t===h.week)x[D.hcSetDate](x[D.hcGetDate]()-x[D.hcGetDay]()+e(b,1));n=x[D.hcGetFullYear]();b=x[D.hcGetMonth](); -var A=x[D.hcGetDate](),F=x[D.hcGetHours]();d=x.getTime();D.hcHasTimeZone&&(q=(!B||!!D.hcGetTimezoneOffset)&&(c-d>4*h.month||w(d)!==w(c)),N=w(x),x=new D(d+N));B=x.getTime();for(d=1;Bk.length&&l(k,function(a){0===a%18E5&&"000000000"===H("%H%M%S%L",a)&&(m[a]="day")})}k.info=r(a,{higherRanks:m,totalRange:t*C});return k}; -E.prototype.normalizeTimeTickInterval=function(a,d){var c=d||[["millisecond",[1,2,5,10,20,25,50,100,200,500]],["second",[1,2,5,10,15,30]],["minute",[1,2,5,10,15,30]],["hour",[1,2,3,4,6,8,12]],["day",[1,2]],["week",[1,2]],["month",[1,2,3,4,6]],["year",null]];d=c[c.length-1];var b=h[d[0]],k=d[1],e;for(e=0;er&&(!w||z<=n)&&void 0!==z&&d.push(z),z>n&& -(B=!0),z=k;else r=h(r),n=h(n),a=w?this.getMinorTickInterval():l.tickInterval,a=f("auto"===a?null:a,this._minorAutoInterval,l.tickPixelInterval/(w?5:1)*(n-r)/((w?e/this.tickPositions.length:e)||1)),a=p(a,null,D(a)),d=H(this.getLinearTickPositions(a,r,n),m),w||(this._minorAutoInterval=a/5);w||(this.tickInterval=a);return d};E.prototype.log2lin=function(a){return Math.log(a)/Math.LN10};E.prototype.lin2log=function(a){return Math.pow(10,a)}})(M);(function(a,E){var D=a.arrayMax,H=a.arrayMin,p=a.defined, -f=a.destroyObjectProperties,l=a.each,r=a.erase,n=a.merge,w=a.pick;a.PlotLineOrBand=function(a,e){this.axis=a;e&&(this.options=e,this.id=e.id)};a.PlotLineOrBand.prototype={render:function(){var f=this,e=f.axis,h=e.horiz,m=f.options,d=m.label,c=f.label,b=m.to,k=m.from,z=m.value,l=p(k)&&p(b),r=p(z),x=f.svgElem,K=!x,t=[],C=m.color,N=w(m.zIndex,0),q=m.events,t={"class":"highcharts-plot-"+(l?"band ":"line ")+(m.className||"")},A={},F=e.chart.renderer,G=l?"bands":"lines",g=e.log2lin;e.isLog&&(k=g(k),b=g(b), -z=g(z));r?(t={stroke:C,"stroke-width":m.width},m.dashStyle&&(t.dashstyle=m.dashStyle)):l&&(C&&(t.fill=C),m.borderWidth&&(t.stroke=m.borderColor,t["stroke-width"]=m.borderWidth));A.zIndex=N;G+="-"+N;(C=e.plotLinesAndBandsGroups[G])||(e.plotLinesAndBandsGroups[G]=C=F.g("plot-"+G).attr(A).add());K&&(f.svgElem=x=F.path().attr(t).add(C));if(r)t=e.getPlotLinePath(z,x.strokeWidth());else if(l)t=e.getPlotBandPath(k,b,m);else return;K&&t&&t.length?(x.attr({d:t}),q&&a.objectEach(q,function(a,b){x.on(b,function(a){q[b].apply(f, -[a])})})):x&&(t?(x.show(),x.animate({d:t})):(x.hide(),c&&(f.label=c=c.destroy())));d&&p(d.text)&&t&&t.length&&0this.max&& -e>this.max;if(m&&h)for(a&&(k=m.toString()===h.toString(),b=0),a=0;aA-h?A:A-h);else if(z)k[a]=Math.max(e,g+h+d>c?g:g+h);else return!1},C=function(a,c,d,g){var e;gc-b?e=!1:k[a]=gc-d/2?c-d-2:g-d/2;return e},p=function(a){var b=f;f=x;x=b;m=a},q=function(){!1!==t.apply(0,f)?!1!==C.apply(0,x)||m||(p(!0),q()):m?k.x=k.y=0:(p(!0),q())};(c.inverted||1q&&(h=!1);a=(e.series&& -e.series.yAxis&&e.series.yAxis.pos)+(e.plotY||0);a-=b.plotTop;c.push({target:e.isHeader?b.plotHeight+l:a,rank:e.isHeader?1:0,size:z.tt.getBBox().height+1,point:e,x:q,tt:t})}});this.cleanSplit();a.distribute(c,b.plotHeight+l);D(c,function(a){var c=a.point,d=c.series;a.tt.attr({visibility:void 0===a.pos?"hidden":"inherit",x:h||c.isHeader?a.x:c.plotX+b.plotLeft+n(f.distance,16),y:a.pos+b.plotTop,anchorX:c.isHeader?c.plotX+b.plotLeft:c.plotX+d.xAxis.pos,anchorY:c.isHeader?a.pos+b.plotTop-15:c.plotY+d.yAxis.pos})})}, -updatePosition:function(a){var e=this.chart,d=this.getLabel(),d=(this.options.positioner||this.getPosition).call(this,d.width,d.height,a);this.move(Math.round(d.x),Math.round(d.y||0),a.plotX+e.plotLeft,a.plotY+e.plotTop)},getDateFormat:function(a,m,d,c){var b=E("%m-%d %H:%M:%S.%L",m),k,h,f={millisecond:15,second:12,minute:9,hour:6,day:3},l="millisecond";for(h in e){if(a===e.week&&+E("%w",m)===d&&"00:00:00.000"===b.substr(6)){h="week";break}if(e[h]>a){h=l;break}if(f[h]&&b.substr(f[h])!=="01-01 00:00:00.000".substr(f[h]))break; -"week"!==h&&(l=h)}h&&(k=c[h]);return k},getXDateFormat:function(a,e,d){e=e.dateTimeLabelFormats;var c=d&&d.closestPointRange;return(c?this.getDateFormat(c,a.x,d.options.startOfWeek,e):e.day)||e.year},tooltipFooterHeaderFormatter:function(a,e){e=e?"footer":"header";var d=a.series,c=d.tooltipOptions,b=c.xDateFormat,k=d.xAxis,h=k&&"datetime"===k.options.type&&f(a.key),m=c[e+"Format"];h&&!b&&(b=this.getXDateFormat(a,c,k));h&&b&&D(a.point&&a.point.tooltipDateKeys||["key"],function(a){m=m.replace("{point."+ -a+"}","{point."+a+":"+b+"}")});return p(m,{point:a,series:d})},bodyFormatter:function(a){return l(a,function(a){var d=a.series.tooltipOptions;return(d[(a.point.formatPrefix||"point")+"Formatter"]||a.point.tooltipFormatter).call(a.point,d[(a.point.formatPrefix||"point")+"Format"])})}}})(M);(function(a){var E=a.addEvent,D=a.attr,H=a.charts,p=a.color,f=a.css,l=a.defined,r=a.each,n=a.extend,w=a.find,u=a.fireEvent,e=a.isObject,h=a.offset,m=a.pick,d=a.splat,c=a.Tooltip;a.Pointer=function(a,c){this.init(a, -c)};a.Pointer.prototype={init:function(a,d){this.options=d;this.chart=a;this.runChartClick=d.chart.events&&!!d.chart.events.click;this.pinchDown=[];this.lastValidTouch={};c&&(a.tooltip=new c(a,d.tooltip),this.followTouchMove=m(d.tooltip.followTouchMove,!0));this.setDOMEvents()},zoomOption:function(a){var b=this.chart,c=b.options.chart,d=c.zoomType||"",b=b.inverted;/touch/.test(a.type)&&(d=m(c.pinchType,d));this.zoomX=a=/x/.test(d);this.zoomY=d=/y/.test(d);this.zoomHor=a&&!b||d&&b;this.zoomVert=d&& -!b||a&&b;this.hasZoom=a||d},normalize:function(a,c){var b;b=a.touches?a.touches.length?a.touches.item(0):a.changedTouches[0]:a;c||(this.chartPosition=c=h(this.chart.container));return n(a,{chartX:Math.round(b.pageX-c.left),chartY:Math.round(b.pageY-c.top)})},getCoordinates:function(a){var b={xAxis:[],yAxis:[]};r(this.chart.axes,function(c){b[c.isXAxis?"xAxis":"yAxis"].push({axis:c,value:c.toValue(a[c.horiz?"chartX":"chartY"])})});return b},findNearestKDPoint:function(a,c,d){var b;r(a,function(a){var k= -!(a.noSharedTooltip&&c)&&0>a.options.findNearestPointBy.indexOf("y");a=a.searchPoint(d,k);if((k=e(a,!0))&&!(k=!e(b,!0)))var k=b.distX-a.distX,h=b.dist-a.dist,m=(a.series.group&&a.series.group.zIndex)-(b.series.group&&b.series.group.zIndex),k=0<(0!==k&&c?k:0!==h?h:0!==m?m:b.series.index>a.series.index?-1:1);k&&(b=a)});return b},getPointFromEvent:function(a){a=a.target;for(var b;a&&!b;)b=a.point,a=a.parentNode;return b},getChartCoordinatesFromPoint:function(a,c){var b=a.series,d=b.xAxis,b=b.yAxis,k= -m(a.clientX,a.plotX);if(d&&b)return c?{chartX:d.len+d.pos-k,chartY:b.len+b.pos-a.plotY}:{chartX:k+d.pos,chartY:a.plotY+b.pos}},getHoverData:function(b,c,d,h,f,l,n){var k,z=[],x=n&&n.isBoosting;h=!(!h||!b);n=c&&!c.stickyTracking?[c]:a.grep(d,function(a){return a.visible&&!(!f&&a.directTouch)&&m(a.options.enableMouseTracking,!0)&&a.stickyTracking});c=(k=h?b:this.findNearestKDPoint(n,f,l))&&k.series;k&&(f&&!c.noSharedTooltip?(n=a.grep(d,function(a){return a.visible&&!(!f&&a.directTouch)&&m(a.options.enableMouseTracking, -!0)&&!a.noSharedTooltip}),r(n,function(a){var b=w(a.points,function(a){return a.x===k.x&&!a.isNull});e(b)&&(x&&(b=a.getPoint(b)),z.push(b))})):z.push(k));return{hoverPoint:k,hoverSeries:c,hoverPoints:z}},runPointActions:function(b,c){var d=this.chart,k=d.tooltip&&d.tooltip.options.enabled?d.tooltip:void 0,e=k?k.shared:!1,h=c||d.hoverPoint,f=h&&h.series||d.hoverSeries,f=this.getHoverData(h,f,d.series,!!c||f&&f.directTouch&&this.isDirectTouch,e,b,{isBoosting:d.isBoosting}),l,h=f.hoverPoint;l=f.hoverPoints; -c=(f=f.hoverSeries)&&f.tooltipOptions.followPointer;e=e&&f&&!f.noSharedTooltip;if(h&&(h!==d.hoverPoint||k&&k.isHidden)){r(d.hoverPoints||[],function(b){-1===a.inArray(b,l)&&b.setState()});r(l||[],function(a){a.setState("hover")});if(d.hoverSeries!==f)f.onMouseOver();d.hoverPoint&&d.hoverPoint.firePointEvent("mouseOut");if(!h.series)return;h.firePointEvent("mouseOver");d.hoverPoints=l;d.hoverPoint=h;k&&k.refresh(e?l:h,b)}else c&&k&&!k.isHidden&&(h=k.getAnchor([{}],b),k.updatePosition({plotX:h[0],plotY:h[1]})); -this.unDocMouseMove||(this.unDocMouseMove=E(d.container.ownerDocument,"mousemove",function(b){var c=H[a.hoverChartIndex];if(c)c.pointer.onDocumentMouseMove(b)}));r(d.axes,function(c){var d=m(c.crosshair.snap,!0),k=d?a.find(l,function(a){return a.series[c.coll]===c}):void 0;k||!d?c.drawCrosshair(b,k):c.hideCrosshair()})},reset:function(a,c){var b=this.chart,k=b.hoverSeries,e=b.hoverPoint,h=b.hoverPoints,m=b.tooltip,f=m&&m.shared?h:e;a&&f&&r(d(f),function(b){b.series.isCartesian&&void 0===b.plotX&& -(a=!1)});if(a)m&&f&&(m.refresh(f),e&&(e.setState(e.state,!0),r(b.axes,function(a){a.crosshair&&a.drawCrosshair(null,e)})));else{if(e)e.onMouseOut();h&&r(h,function(a){a.setState()});if(k)k.onMouseOut();m&&m.hide(c);this.unDocMouseMove&&(this.unDocMouseMove=this.unDocMouseMove());r(b.axes,function(a){a.hideCrosshair()});this.hoverX=b.hoverPoints=b.hoverPoint=null}},scaleGroups:function(a,c){var b=this.chart,d;r(b.series,function(k){d=a||k.getPlotBox();k.xAxis&&k.xAxis.zoomEnabled&&k.group&&(k.group.attr(d), -k.markerGroup&&(k.markerGroup.attr(d),k.markerGroup.clip(c?b.clipRect:null)),k.dataLabelsGroup&&k.dataLabelsGroup.attr(d))});b.clipRect.attr(c||b.clipBox)},dragStart:function(a){var b=this.chart;b.mouseIsDown=a.type;b.cancelClick=!1;b.mouseDownX=this.mouseDownX=a.chartX;b.mouseDownY=this.mouseDownY=a.chartY},drag:function(a){var b=this.chart,c=b.options.chart,d=a.chartX,e=a.chartY,h=this.zoomHor,m=this.zoomVert,f=b.plotLeft,l=b.plotTop,n=b.plotWidth,q=b.plotHeight,A,F=this.selectionMarker,G=this.mouseDownX, -g=this.mouseDownY,v=c.panKey&&a[c.panKey+"Key"];F&&F.touch||(df+n&&(d=f+n),el+q&&(e=l+q),this.hasDragged=Math.sqrt(Math.pow(G-d,2)+Math.pow(g-e,2)),10u.max&&(f=u.max-x,v=!0);v?(F-=.8*(F-m[b][0]),q||(g-=.8*(g-m[b][1])),l()):m[b]=[F,g];C||(h[b]=w-r,h[n]=x);h=C?1/t:t;e[n]=x;e[b]=f;p[C?a?"scaleY":"scaleX":"scale"+k]=t;p["translate"+k]=h*r+(F-h*A)},pinch:function(a){var n=this,r=n.chart,u=n.pinchDown,e=a.touches,h=e.length,m=n.lastValidTouch, -d=n.hasZoom,c=n.selectionMarker,b={},k=1===h&&(n.inClass(a.target,"highcharts-tracker")&&r.runTrackerClick||n.runChartClick),z={};1c-6&&h(q||c.spacingBox.width-2*t-d.x)&&(this.itemX=t,this.itemY+=F+this.lastLineHeight+A,this.lastLineHeight=0);this.maxItemWidth=Math.max(this.maxItemWidth,m);this.lastItemY=F+this.itemY+A;this.lastLineHeight=Math.max(b,this.lastLineHeight);a._legendItemPos=[this.itemX,this.itemY];e?this.itemX+=m:(this.itemY+=F+b+A,this.lastLineHeight=b);this.offsetWidth=q||Math.max((e?this.itemX-t-(a.checkbox?0:p):m)+t,this.offsetWidth)}, -getAllItems:function(){var a=[];f(this.chart.series,function(c){var b=c&&c.options;c&&w(b.showInLegend,p(b.linkedTo)?!1:void 0,!0)&&(a=a.concat(c.legendItems||("point"===b.legendType?c.data:c)))});return a},getAlignment:function(){var a=this.options;return a.floating?"":a.align.charAt(0)+a.verticalAlign.charAt(0)+a.layout.charAt(0)},adjustMargins:function(a,c){var b=this.chart,d=this.options,e=this.getAlignment();e&&f([/(lth|ct|rth)/,/(rtv|rm|rbv)/,/(rbh|cb|lbh)/,/(lbv|lm|ltv)/],function(k,h){k.test(e)&& -!p(a[h])&&(b[r[h]]=Math.max(b[r[h]],b.legend[(h+1)%2?"legendHeight":"legendWidth"]+[1,-1,-1,1][h]*d[h%2?"x":"y"]+w(d.margin,12)+c[h]+(0===h?b.titleOffset+b.options.title.margin:0)))})},render:function(){var a=this,c=a.chart,b=c.renderer,k=a.group,h,m,l,x,p=a.box,t=a.options,C=a.padding;a.itemX=C;a.itemY=a.initialItemY;a.offsetWidth=0;a.lastItemY=0;k||(a.group=k=b.g("legend").attr({zIndex:7}).add(),a.contentGroup=b.g().attr({zIndex:1}).add(k),a.scrollGroup=b.g().add(a.contentGroup));a.renderTitle(); -h=a.getAllItems();e(h,function(a,b){return(a.options&&a.options.legendIndex||0)-(b.options&&b.options.legendIndex||0)});t.reversed&&h.reverse();a.allItems=h;a.display=m=!!h.length;a.lastLineHeight=0;f(h,function(b){a.renderItem(b)});l=(t.width||a.offsetWidth)+C;x=a.lastItemY+a.lastLineHeight+a.titleHeight;x=a.handleOverflow(x);x+=C;p||(a.box=p=b.rect().addClass("highcharts-legend-box").attr({r:t.borderRadius}).add(k),p.isNew=!0);p.attr({stroke:t.borderColor,"stroke-width":t.borderWidth||0,fill:t.backgroundColor|| -"none"}).shadow(t.shadow);0b&&!1!==t.enabled?(this.clipHeight=l=Math.max(b-20-this.titleHeight-m,0),this.currentPage=w(this.currentPage,1),this.fullHeight=a,f(G,function(a,b){var c=a._legendItemPos[1],d=Math.round(a.legendItem.getBBox().height),g=A.length;if(!g||c-A[g-1]>l&&(F||c)!==A[g-1])A.push(F||c),g++;a.pageIx=g-1;F&&(G[b-1].pageIx=g-1);b===G.length-1&&c+d-A[g-1]>l&&(A.push(c),a.pageIx=g);c!==F&&(F=c)}),n||(n=c.clipRect=d.clipRect(0,m,9999,0),c.contentGroup.clip(n)),g(l),q||(this.nav=q=d.g().attr({zIndex:1}).add(this.group), -this.up=d.symbol("triangle",0,0,r,r).on("click",function(){c.scroll(-1,p)}).add(q),this.pager=d.text("",15,10).addClass("highcharts-legend-navigation").css(t.style).add(q),this.down=d.symbol("triangle-down",0,0,r,r).on("click",function(){c.scroll(1,p)}).add(q)),c.scroll(0),a=b):q&&(g(),this.nav=q.destroy(),this.scrollGroup.attr({translateY:1}),this.clipHeight=0);return a},scroll:function(a,c){var b=this.pages,d=b.length;a=this.currentPage+a;var e=this.clipHeight,h=this.options.navigation,f=this.pager, -m=this.padding;a>d&&(a=d);0b&&(f=typeof a[0],"string"===f?e.name=a[0]:"number"===f&&(e.x=a[0]),k++);l=d.value;)d=h[++f];d&&d.color&&!this.options.color&&(this.color=d.color);return d},destroy:function(){var a=this.series.chart,h=a.hoverPoints,f;a.pointCount--;h&&(this.setState(),p(h,this),h.length||(a.hoverPoints=null));if(this===a.hoverPoint)this.onMouseOut();if(this.graphic||this.dataLabel)u(this),this.destroyElements();this.legendItem&&a.legend.destroyItem(this);for(f in this)this[f]=null},destroyElements:function(){for(var a=["graphic","dataLabel", -"dataLabelUpper","connector","shadowGroup"],h,f=6;f--;)h=a[f],this[h]&&(this[h]=this[h].destroy())},getLabelConfig:function(){return{x:this.category,y:this.y,color:this.color,colorIndex:this.colorIndex,key:this.name||this.category,series:this.series,point:this,percentage:this.percentage,total:this.total||this.stackTotal}},tooltipFormatter:function(a){var e=this.series,f=e.tooltipOptions,d=w(f.valueDecimals,""),c=f.valuePrefix||"",b=f.valueSuffix||"";D(e.pointArrayMap||["y"],function(e){e="{point."+ -e;if(c||b)a=a.replace(e+"}",c+e+"}"+b);a=a.replace(e+"}",e+":,."+d+"f}")});return l(a,{point:this,series:this.series})},firePointEvent:function(a,h,m){var d=this,c=this.series.options;(c.point.events[a]||d.options&&d.options.events&&d.options.events[a])&&this.importEvents();"click"===a&&c.allowPointSelect&&(m=function(a){d.select&&d.select(null,a.ctrlKey||a.metaKey||a.shiftKey)});f(this,a,h,m)},visible:!0}})(M);(function(a){var E=a.addEvent,D=a.animObject,H=a.arrayMax,p=a.arrayMin,f=a.correctFloat, -l=a.Date,r=a.defaultOptions,n=a.defaultPlotOptions,w=a.defined,u=a.each,e=a.erase,h=a.extend,m=a.fireEvent,d=a.grep,c=a.isArray,b=a.isNumber,k=a.isString,z=a.merge,B=a.objectEach,I=a.pick,x=a.removeEvent,K=a.splat,t=a.SVGElement,C=a.syncTimeout,N=a.win;a.Series=a.seriesType("line",null,{lineWidth:2,allowPointSelect:!1,showCheckbox:!1,animation:{duration:1E3},events:{},marker:{lineWidth:0,lineColor:"#ffffff",radius:4,states:{hover:{animation:{duration:50},enabled:!0,radiusPlus:2,lineWidthPlus:1},select:{fillColor:"#cccccc", -lineColor:"#000000",lineWidth:2}}},point:{events:{}},dataLabels:{align:"center",formatter:function(){return null===this.y?"":a.numberFormat(this.y,-1)},style:{fontSize:"11px",fontWeight:"bold",color:"contrast",textOutline:"1px contrast"},verticalAlign:"bottom",x:0,y:0,padding:5},cropThreshold:300,pointRange:0,softThreshold:!0,states:{hover:{animation:{duration:50},lineWidthPlus:1,marker:{},halo:{size:10,opacity:.25}},select:{marker:{}}},stickyTracking:!0,turboThreshold:1E3,findNearestPointBy:"x"}, -{isCartesian:!0,pointClass:a.Point,sorted:!0,requireSorting:!0,directTouch:!1,axisTypes:["xAxis","yAxis"],colorCounter:0,parallelArrays:["x","y"],coll:"series",init:function(a,b){var c=this,d,g=a.series,e;c.chart=a;c.options=b=c.setOptions(b);c.linkedSeries=[];c.bindAxes();h(c,{name:b.name,state:"",visible:!1!==b.visible,selected:!0===b.selected});d=b.events;B(d,function(a,b){E(c,b,a)});if(d&&d.click||b.point&&b.point.events&&b.point.events.click||b.allowPointSelect)a.runTrackerClick=!0;c.getColor(); -c.getSymbol();u(c.parallelArrays,function(a){c[a+"Data"]=[]});c.setData(b.data,!1);c.isCartesian&&(a.hasCartesianSeries=!0);g.length&&(e=g[g.length-1]);c._i=I(e&&e._i,-1)+1;a.orderSeries(this.insert(g))},insert:function(a){var c=this.options.index,d;if(b(c)){for(d=a.length;d--;)if(c>=I(a[d].options.index,a[d]._i)){a.splice(d+1,0,this);break}-1===d&&a.unshift(this);d+=1}else a.push(this);return I(d,a.length-1)},bindAxes:function(){var b=this,c=b.options,d=b.chart,e;u(b.axisTypes||[],function(g){u(d[g], -function(a){e=a.options;if(c[g]===e.index||void 0!==c[g]&&c[g]===e.id||void 0===c[g]&&0===e.index)b.insert(a.series),b[g]=a,a.isDirty=!0});b[g]||b.optionalAxis===g||a.error(18,!0)})},updateParallelArrays:function(a,c){var d=a.series,e=arguments,g=b(c)?function(b){var g="y"===b&&d.toYData?d.toYData(a):a[b];d[b+"Data"][c]=g}:function(a){Array.prototype[c].apply(d[a+"Data"],Array.prototype.slice.call(e,2))};u(d.parallelArrays,g)},autoIncrement:function(){var b=this.options,c=this.xIncrement,d,e=b.pointIntervalUnit, -g=0,c=I(c,b.pointStart,0);this.pointInterval=d=I(this.pointInterval,b.pointInterval,1);e&&(b=new l(c),"day"===e?b=+b[l.hcSetDate](b[l.hcGetDate]()+d):"month"===e?b=+b[l.hcSetMonth](b[l.hcGetMonth]()+d):"year"===e&&(b=+b[l.hcSetFullYear](b[l.hcGetFullYear]()+d)),l.hcHasTimeZone&&(g=a.getTZOffset(b)-a.getTZOffset(c)),d=b-c+g);this.xIncrement=c+d;return c},setOptions:function(a){var b=this.chart,c=b.options,d=c.plotOptions,g=(b.userOptions||{}).plotOptions||{},e=d[this.type];this.userOptions=a;b=z(e, -d.series,a);this.tooltipOptions=z(r.tooltip,r.plotOptions.series&&r.plotOptions.series.tooltip,r.plotOptions[this.type].tooltip,c.tooltip.userOptions,d.series&&d.series.tooltip,d[this.type].tooltip,a.tooltip);this.stickyTracking=I(a.stickyTracking,g[this.type]&&g[this.type].stickyTracking,g.series&&g.series.stickyTracking,this.tooltipOptions.shared&&!this.noSharedTooltip?!0:b.stickyTracking);null===e.marker&&delete b.marker;this.zoneAxis=b.zoneAxis;a=this.zones=(b.zones||[]).slice();!b.negativeColor&& -!b.negativeFillColor||b.zones||a.push({value:b[this.zoneAxis+"Threshold"]||b.threshold||0,className:"highcharts-negative",color:b.negativeColor,fillColor:b.negativeFillColor});a.length&&w(a[a.length-1].value)&&a.push({color:this.color,fillColor:this.fillColor});return b},getCyclic:function(a,b,c){var d,g=this.chart,e=this.userOptions,k=a+"Index",h=a+"Counter",q=c?c.length:I(g.options.chart[a+"Count"],g[a+"Count"]);b||(d=I(e[k],e["_"+k]),w(d)||(g.series.length||(g[h]=0),e["_"+k]=d=g[h]%q,g[h]+=1), -c&&(b=c[d]));void 0!==d&&(this[k]=d);this[a]=b},getColor:function(){this.options.colorByPoint?this.options.color=null:this.getCyclic("color",this.options.color||n[this.type].color,this.chart.options.colors)},getSymbol:function(){this.getCyclic("symbol",this.options.marker.symbol,this.chart.options.symbols)},drawLegendSymbol:a.LegendSymbolMixin.drawLineMarker,setData:function(d,e,h,f){var g=this,q=g.points,m=q&&q.length||0,l,A=g.options,t=g.chart,x=null,n=g.xAxis,p=A.turboThreshold,z=this.xData,F= -this.yData,C=(l=g.pointArrayMap)&&l.length;d=d||[];l=d.length;e=I(e,!0);if(!1!==f&&l&&m===l&&!g.cropped&&!g.hasGroupedData&&g.visible)u(d,function(a,b){q[b].update&&a!==A.data[b]&&q[b].update(a,!1,null,!1)});else{g.xIncrement=null;g.colorCounter=0;u(this.parallelArrays,function(a){g[a+"Data"].length=0});if(p&&l>p){for(h=0;null===x&&hf||this.forceCrop))if(c[e-1]z)c=[],d=[];else if(c[0]z)g=this.cropData(this.xData,this.yData,p,z),c=g.xData,d=g.yData,g=g.start,k=!0;for(f=c.length|| -1;--f;)e=x?m(c[f])-m(c[f-1]):c[f]-c[f-1],0e&&n&&(a.error(15),n=!1);this.cropped=k;this.cropStart=g;this.processedXData=c;this.processedYData=d;this.closestPointRange=h},cropData:function(a,b,c,d){var g=a.length,e=0,k=g,h=I(this.cropShoulder,1),f;for(f=0;f=c){e=Math.max(0,f-h);break}for(c=f;cd){k=c+h;break}return{xData:a.slice(e,k),yData:b.slice(e,k),start:e,end:k}},generatePoints:function(){var a=this.options,b=a.data,c=this.data,d,g=this.processedXData, -e=this.processedYData,k=this.pointClass,h=g.length,f=this.cropStart||0,m,l=this.hasGroupedData,a=a.keys,t,x=[],n;c||l||(c=[],c.length=b.length,c=this.data=c);a&&l&&(this.options.keys=!1);for(n=0;n=f&&(e[n-1]||l)<=q,m&&l)if(m=t.length)for(;m--;)"number"===typeof t[m]&&(g[h++]=t[m]);else g[h++]=t;this.dataMin= -p(g);this.dataMax=H(g)},translate:function(){this.processedXData||this.processData();this.generatePoints();var a=this.options,c=a.stacking,d=this.xAxis,e=d.categories,g=this.yAxis,k=this.points,h=k.length,m=!!this.modifyValue,l=a.pointPlacement,t="between"===l||b(l),n=a.threshold,x=a.startFromThreshold?n:0,p,z,C,r,u=Number.MAX_VALUE;"between"===l&&(l=.5);b(l)&&(l*=I(a.pointRange||d.pointRange));for(a=0;a=K&&(B.isNull=!0);B.plotX=p=f(Math.min(Math.max(-1E5,d.translate(N,0,0,0,1,l,"flags"===this.type)),1E5));c&&this.visible&&!B.isNull&&D&&D[N]&&(r=this.getStackIndicator(r,N,this.index),E=D[N],K=E.points[r.key],z=K[0],K=K[1],z===x&&r.key===D[N].base&&(z=I(n,g.min)),g.positiveValuesOnly&&0>=z&&(z=null),B.total=B.stackTotal=E.total,B.percentage=E.total&&B.y/E.total*100,B.stackY=K,E.setOffset(this.pointXOffset||0,this.barW||0));B.yBottom=w(z)?g.translate(z,0,1,0,1): -null;m&&(K=this.modifyValue(K,B));B.plotY=z="number"===typeof K&&Infinity!==K?Math.min(Math.max(-1E5,g.translate(K,0,1,0,1)),1E5):void 0;B.isInside=void 0!==z&&0<=z&&z<=g.len&&0<=p&&p<=d.len;B.clientX=t?f(d.translate(N,0,0,0,1,l)):p;B.negative=B.y<(n||0);B.category=e&&void 0!==e[B.x]?e[B.x]:B.x;B.isNull||(void 0!==C&&(u=Math.min(u,Math.abs(p-C))),C=p);B.zone=this.zones.length&&B.getZone()}this.closestPointRangePx=u},getValidPoints:function(a,b){var c=this.chart;return d(a||this.points||[],function(a){return b&& -!c.isInsidePlot(a.plotX,a.plotY,c.inverted)?!1:!a.isNull})},setClip:function(a){var b=this.chart,c=this.options,d=b.renderer,g=b.inverted,e=this.clipBox,k=e||b.clipBox,h=this.sharedClipKey||["_sharedClip",a&&a.duration,a&&a.easing,k.height,c.xAxis,c.yAxis].join(),f=b[h],q=b[h+"m"];f||(a&&(k.width=0,g&&(k.x=b.plotSizeX),b[h+"m"]=q=d.clipRect(g?b.plotSizeX+99:-99,g?-b.plotLeft:-b.plotTop,99,g?b.chartWidth:b.chartHeight)),b[h]=f=d.clipRect(k),f.count={length:0});a&&!f.count[this.index]&&(f.count[this.index]= -!0,f.count.length+=1);!1!==c.clip&&(this.group.clip(a||e?f:b.clipRect),this.markerGroup.clip(q),this.sharedClipKey=h);a||(f.count[this.index]&&(delete f.count[this.index],--f.count.length),0===f.count.length&&h&&b[h]&&(e||(b[h]=b[h].destroy()),b[h+"m"]&&(b[h+"m"]=b[h+"m"].destroy())))},animate:function(a){var b=this.chart,c=D(this.options.animation),d;a?this.setClip(c):(d=this.sharedClipKey,(a=b[d])&&a.animate({width:b.plotSizeX,x:0},c),b[d+"m"]&&b[d+"m"].animate({width:b.plotSizeX+99,x:0},c),this.animate= -null)},afterAnimate:function(){this.setClip();m(this,"afterAnimate");this.finishedAnimating=!0},drawPoints:function(){var a=this.points,b=this.chart,c,d,g,e,k=this.options.marker,h,f,l,m=this[this.specialGroup]||this.markerGroup,t,n=I(k.enabled,this.xAxis.isRadial?!0:null,this.closestPointRangePx>=2*k.radius);if(!1!==k.enabled||this._hasPointMarkers)for(c=0;ce&&b.shadow));k&&(k.startX=c.xMap,k.isArea=c.isArea)})}, -applyZones:function(){var a=this,b=this.chart,c=b.renderer,d=this.zones,e,k,h=this.clips||[],f,m=this.graph,l=this.area,t=Math.max(b.chartWidth,b.chartHeight),n=this[(this.zoneAxis||"y")+"Axis"],x,p,z=b.inverted,C,r,w,B,K=!1;d.length&&(m||l)&&n&&void 0!==n.min&&(p=n.reversed,C=n.horiz,m&&m.hide(),l&&l.hide(),x=n.getExtremes(),u(d,function(d,g){e=p?C?b.plotWidth:0:C?0:n.toPixels(x.min);e=Math.min(Math.max(I(k,e),0),t);k=Math.min(Math.max(Math.round(n.toPixels(I(d.value,x.max),!0)),0),t);K&&(e=k=n.toPixels(x.max)); -r=Math.abs(e-k);w=Math.min(e,k);B=Math.max(e,k);n.isXAxis?(f={x:z?B:w,y:0,width:r,height:t},C||(f.x=b.plotHeight-f.x)):(f={x:0,y:z?B:w,width:t,height:r},C&&(f.y=b.plotWidth-f.y));z&&c.isVML&&(f=n.isXAxis?{x:0,y:p?w:B,height:f.width,width:b.chartWidth}:{x:f.y-b.plotLeft-b.spacingBox.x,y:0,width:f.height,height:b.chartHeight});h[g]?h[g].animate(f):(h[g]=c.clipRect(f),m&&a["zone-graph-"+g].clip(h[g]),l&&a["zone-area-"+g].clip(h[g]));K=d.value>x.max}),this.clips=h)},invertGroups:function(a){function b(){u(["group", -"markerGroup"],function(b){c[b]&&(d.renderer.isVML&&c[b].attr({width:c.yAxis.len,height:c.xAxis.len}),c[b].width=c.yAxis.len,c[b].height=c.xAxis.len,c[b].invert(a))})}var c=this,d=c.chart,e;c.xAxis&&(e=E(d,"resize",b),E(c,"destroy",e),b(a),c.invertGroups=b)},plotGroup:function(a,b,c,d,e){var g=this[a],k=!g;k&&(this[a]=g=this.chart.renderer.g().attr({zIndex:d||.1}).add(e));g.addClass("highcharts-"+b+" highcharts-series-"+this.index+" highcharts-"+this.type+"-series "+(w(this.colorIndex)?"highcharts-color-"+ -this.colorIndex+" ":"")+(this.options.className||"")+(g.hasClass("highcharts-tracker")?" highcharts-tracker":""),!0);g.attr({visibility:c})[k?"attr":"animate"](this.getPlotBox());return g},getPlotBox:function(){var a=this.chart,b=this.xAxis,c=this.yAxis;a.inverted&&(b=c,c=this.xAxis);return{translateX:b?b.left:a.plotLeft,translateY:c?c.top:a.plotTop,scaleX:1,scaleY:1}},render:function(){var a=this,b=a.chart,c,d=a.options,e=!!a.animate&&b.renderer.isSVG&&D(d.animation).duration,k=a.visible?"inherit": -"hidden",h=d.zIndex,f=a.hasRendered,m=b.seriesGroup,l=b.inverted;c=a.plotGroup("group","series",k,h,m);a.markerGroup=a.plotGroup("markerGroup","markers",k,h,m);e&&a.animate(!0);c.inverted=a.isCartesian?l:!1;a.drawGraph&&(a.drawGraph(),a.applyZones());a.drawDataLabels&&a.drawDataLabels();a.visible&&a.drawPoints();a.drawTracker&&!1!==a.options.enableMouseTracking&&a.drawTracker();a.invertGroups(l);!1===d.clip||a.sharedClipKey||f||c.clip(b.clipRect);e&&a.animate();f||(a.animationTimeout=C(function(){a.afterAnimate()}, -e));a.isDirty=!1;a.hasRendered=!0},redraw:function(){var a=this.chart,b=this.isDirty||this.isDirtyData,c=this.group,d=this.xAxis,e=this.yAxis;c&&(a.inverted&&c.attr({width:a.plotWidth,height:a.plotHeight}),c.animate({translateX:I(d&&d.left,a.plotLeft),translateY:I(e&&e.top,a.plotTop)}));this.translate();this.render();b&&delete this.kdTree},kdAxisArray:["clientX","plotY"],searchPoint:function(a,b){var c=this.xAxis,d=this.yAxis,e=this.chart.inverted;return this.searchKDTree({clientX:e?c.len-a.chartY+ -c.pos:a.chartX-c.pos,plotY:e?d.len-a.chartX+d.pos:a.chartY-d.pos},b)},buildKDTree:function(){function a(c,d,e){var g,k;if(k=c&&c.length)return g=b.kdAxisArray[d%e],c.sort(function(a,b){return a[g]-b[g]}),k=Math.floor(k/2),{point:c[k],left:a(c.slice(0,k),d+1,e),right:a(c.slice(k+1),d+1,e)}}this.buildingKdTree=!0;var b=this,c=-1l?"left":"right";t=0>l?"right":"left";b[q]&&(q=c(a,b[q],g+1,f),n=q[h]x;)q--;this.updateParallelArrays(m,"splice",q,0,0);this.updateParallelArrays(m,q);g&&m.name&&(g[x]=m.name);l.splice(q,0,a);n&&(this.data.splice(q,0,null),this.processData());"point"===e.legendType&&this.generatePoints();c&&(h[0]&&h[0].remove?h[0].remove(!1):(h.shift(),this.updateParallelArrays(m,"shift"),l.shift()));this.isDirtyData=this.isDirty=!0;b&&f.redraw(d)},removePoint:function(a,b,c){var d=this,e=d.data,h=e[a],f=d.points,g=d.chart,l=function(){f&&f.length===e.length&& -f.splice(a,1);e.splice(a,1);d.options.data.splice(a,1);d.updateParallelArrays(h||{series:d},"splice",a,1);h&&h.destroy();d.isDirty=!0;d.isDirtyData=!0;b&&g.redraw()};x(c,g);b=k(b,!0);h?h.firePointEvent("remove",null,l):l()},remove:function(a,b,c){function d(){e.destroy();h.isDirtyLegend=h.isDirtyBox=!0;h.linkSeries();k(a,!0)&&h.redraw(b)}var e=this,h=e.chart;!1!==c?u(e,"remove",null,d):d()},update:function(a,b){var d=this,e=d.chart,h=d.userOptions,f=d.oldType||d.type,l=a.type||h.type||e.options.chart.type, -g=I[f].prototype,m,n=["group","markerGroup","dataLabelsGroup"],t=["navigatorSeries","baseSeries"],x=d.finishedAnimating&&{animation:!1};if(Object.keys&&"data"===Object.keys(a).toString())return this.setData(a.data,b);t=n.concat(t);r(t,function(a){t[a]=d[a];delete d[a]});a=c(h,x,{index:d.index,pointStart:d.xData[0]},{data:d.options.data},a);d.remove(!1,null,!1);for(m in g)d[m]=void 0;w(d,I[l||f].prototype);r(t,function(a){d[a]=t[a]});d.init(e,a);a.zIndex!==h.zIndex&&r(n,function(b){d[b]&&d[b].attr({zIndex:a.zIndex})}); -d.oldType=f;e.linkSeries();k(b,!0)&&e.redraw(!1)}});w(H.prototype,{update:function(a,b){var d=this.chart;a=d.options[this.coll][this.options.index]=c(this.userOptions,a);this.destroy(!0);this.init(d,w(a,{events:void 0}));d.isDirtyBox=!0;k(b,!0)&&d.redraw()},remove:function(a){for(var b=this.chart,c=this.coll,e=this.series,h=e.length;h--;)e[h]&&e[h].remove(!1);n(b.axes,this);n(b[c],this);d(b.options[c])?b.options[c].splice(this.options.index,1):delete b.options[c];r(b[c],function(a,b){a.options.index= -b});this.destroy();b.isDirtyBox=!0;k(a,!0)&&b.redraw()},setTitle:function(a,b){this.update({title:a},b)},setCategories:function(a,b){this.update({categories:a},b)}})})(M);(function(a){var E=a.color,D=a.each,H=a.map,p=a.pick,f=a.Series,l=a.seriesType;l("area","line",{softThreshold:!1,threshold:0},{singleStacks:!1,getStackPoints:function(f){var l=[],r=[],u=this.xAxis,e=this.yAxis,h=e.stacks[this.stackKey],m={},d=this.index,c=e.series,b=c.length,k,z=p(e.options.reversedStacks,!0)?1:-1,B;f=f||this.points; -if(this.options.stacking){for(B=0;Ba&&w>l?(w=Math.max(a,l),e=2*l-w):wp&&e>l?(e=Math.max(p,l),w=2*l-e):e=Math.abs(h)&&.5a.closestPointRange*a.xAxis.transA,d=a.borderWidth=r(f.borderWidth,d?0:1),c=a.yAxis,b=f.threshold,k=a.translatedThreshold=c.getThreshold(b),l=r(f.minPointLength,5),p=a.getColumnMetrics(),u=p.width,x=a.barW=Math.max(u,1+2*d),w=a.pointXOffset=p.offset;h.inverted&&(k-=.5);f.pointPadding&&(x=Math.ceil(x));n.prototype.translate.apply(a);H(a.points,function(d){var e=r(d.yBottom,k),f=999+Math.abs(e),f=Math.min(Math.max(-f,d.plotY),c.len+f),m=d.plotX+w,n=x,t=Math.min(f,e),p,g=Math.max(f,e)-t;l&& -Math.abs(g)l?e-l:k-(p?l:0));d.barX=m;d.pointWidth=u;d.tooltipPos=h.inverted?[c.len+c.pos-h.plotLeft-f,a.xAxis.len-m-n/2,g]:[m+n/2,f+c.pos-h.plotTop,g];d.shapeType="rect";d.shapeArgs=a.crispCol.apply(a,d.isNull?[m,k,n,0]:[m,t,n,g])})},getSymbol:a.noop,drawLegendSymbol:a.LegendSymbolMixin.drawRectangle,drawGraph:function(){this.group[this.dense?"addClass":"removeClass"]("highcharts-dense-data")}, -pointAttribs:function(a,f){var e=this.options,d,c=this.pointAttrToOptions||{};d=c.stroke||"borderColor";var b=c["stroke-width"]||"borderWidth",k=a&&a.color||this.color,h=a&&a[d]||e[d]||this.color||k,n=a&&a[b]||e[b]||this[b]||0,c=e.dashStyle;a&&this.zones.length&&(k=a.getZone(),k=a.options.color||k&&k.color||this.color);f&&(a=l(e.states[f],a.options.states&&a.options.states[f]||{}),f=a.brightness,k=a.color||void 0!==f&&D(k).brighten(a.brightness).get()||k,h=a[d]||h,n=a[b]||n,c=a.dashStyle||c);d={fill:k, -stroke:h,"stroke-width":n};c&&(d.dashstyle=c);return d},drawPoints:function(){var a=this,h=this.chart,m=a.options,d=h.renderer,c=m.animationLimit||250,b;H(a.points,function(e){var k=e.graphic;if(f(e.plotY)&&null!==e.y){b=e.shapeArgs;if(k)k[h.pointCounte;++e)h=w[e],a=2>e||2===e&&/%$/.test(h),w[e]=p(h,[n,l,u,w[2]][e])+(a?r:0);w[3]>w[2]&&(w[3]=w[2]);return w},getStartAndEndRadians:function(a,l){a=D(a)?a:0;l=D(l)&&l>a&&360>l-a?l:a+360;return{start:E*(a+-90),end:E*(l+-90)}}}})(M);(function(a){var E=a.addEvent,D=a.CenteredSeriesMixin,H=a.defined,p=a.each,f=a.extend,l=D.getStartAndEndRadians,r=a.inArray,n=a.noop,w=a.pick,u=a.Point,e=a.Series,h=a.seriesType,m=a.setAnimation;h("pie","line",{center:[null,null],clip:!1,colorByPoint:!0,dataLabels:{distance:30, -enabled:!0,formatter:function(){return this.point.isNull?void 0:this.point.name},x:0},ignoreHiddenPoint:!0,legendType:"point",marker:null,size:null,showInLegend:!1,slicedOffset:10,stickyTracking:!1,tooltip:{followPointer:!0},borderColor:"#ffffff",borderWidth:1,states:{hover:{brightness:.1,shadow:!1}}},{isCartesian:!1,requireSorting:!1,directTouch:!0,noSharedTooltip:!0,trackerGroups:["group","dataLabelsGroup"],axisTypes:[],pointAttribs:a.seriesTypes.column.prototype.pointAttribs,animate:function(a){var c= -this,b=c.points,d=c.startAngleRad;a||(p(b,function(a){var b=a.graphic,e=a.shapeArgs;b&&(b.attr({r:a.startR||c.center[3]/2,start:d,end:d}),b.animate({r:e.r,start:e.start,end:e.end},c.options.animation))}),c.animate=null)},updateTotals:function(){var a,c=0,b=this.points,e=b.length,f,h=this.options.ignoreHiddenPoint;for(a=0;a1.5*Math.PI?m-=2*Math.PI:m<-Math.PI/2&&(m+=2*Math.PI);G.slicedTranslation={translateX:Math.round(Math.cos(m)*d),translateY:Math.round(Math.sin(m)*d)};h=Math.cos(m)*a[2]/ -2;u=Math.sin(m)*a[2]/2;G.tooltipPos=[a[0]+.7*h,a[1]+.7*u];G.half=m<-Math.PI/2||m>Math.PI/2?1:0;G.angle=m;f=Math.min(e,G.labelDistance/5);G.labelPos=[a[0]+h+Math.cos(m)*G.labelDistance,a[1]+u+Math.sin(m)*G.labelDistance,a[0]+h+Math.cos(m)*f,a[1]+u+Math.sin(m)*f,a[0]+h,a[1]+u,0>G.labelDistance?"center":G.half?"right":"left",m]}},drawGraph:null,drawPoints:function(){var a=this,c=a.chart.renderer,b,e,h,l,m=a.options.shadow;m&&!a.shadowGroup&&(a.shadowGroup=c.g("shadow").add(a.group));p(a.points,function(d){e= -d.graphic;if(d.isNull)e&&(d.graphic=e.destroy());else{l=d.shapeArgs;b=d.getTranslate();var k=d.shadowGroup;m&&!k&&(k=d.shadowGroup=c.g("shadow").add(a.shadowGroup));k&&k.attr(b);h=a.pointAttribs(d,d.selected&&"select");e?e.setRadialReference(a.center).attr(h).animate(f(l,b)):(d.graphic=e=c[d.shapeType](l).setRadialReference(a.center).attr(b).add(a.group),d.visible||e.attr({visibility:"hidden"}),e.attr(h).attr({"stroke-linejoin":"round"}).shadow(m,k));e.addClass(d.getClassName())}})},searchPoint:n, -sortByAngle:function(a,c){a.sort(function(a,d){return void 0!==a.angle&&(d.angle-a.angle)*c})},drawLegendSymbol:a.LegendSymbolMixin.drawRectangle,getCenter:D.getCenter,getSymbol:n},{init:function(){u.prototype.init.apply(this,arguments);var a=this,c;a.name=w(a.name,"Slice");c=function(b){a.slice("select"===b.type)};E(a,"select",c);E(a,"unselect",c);return a},isValid:function(){return a.isNumber(this.y,!0)&&0<=this.y},setVisible:function(a,c){var b=this,d=b.series,e=d.chart,f=d.options.ignoreHiddenPoint; -c=w(c,f);a!==b.visible&&(b.visible=b.options.visible=a=void 0===a?!b.visible:a,d.options.data[r(b,d.data)]=b.options,p(["graphic","dataLabel","connector","shadowGroup"],function(c){if(b[c])b[c][a?"show":"hide"](!0)}),b.legendItem&&e.legend.colorizeItem(b,a),a||"hover"!==b.state||b.setState(""),f&&(d.isDirty=!0),c&&e.redraw())},slice:function(a,c,b){var d=this.series;m(b,d.chart);w(c,!0);this.sliced=this.options.sliced=H(a)?a:!this.sliced;d.options.data[r(this,d.data)]=this.options;this.graphic.animate(this.getTranslate()); -this.shadowGroup&&this.shadowGroup.animate(this.getTranslate())},getTranslate:function(){return this.sliced?this.slicedTranslation:{translateX:0,translateY:0}},haloPath:function(a){var c=this.shapeArgs;return this.sliced||!this.visible?[]:this.series.chart.renderer.symbols.arc(c.x,c.y,c.r+a,c.r+a,{innerR:this.shapeArgs.r-1,start:c.start,end:c.end})}})})(M);(function(a){var E=a.addEvent,D=a.arrayMax,H=a.defined,p=a.each,f=a.extend,l=a.format,r=a.map,n=a.merge,w=a.noop,u=a.pick,e=a.relativeLength,h= -a.Series,m=a.seriesTypes,d=a.stableSort;a.distribute=function(a,b){function c(a,b){return a.target-b.target}var e,f=!0,h=a,l=[],m;m=0;for(e=a.length;e--;)m+=a[e].size;if(m>b){d(a,function(a,b){return(b.rank||0)-(a.rank||0)});for(m=e=0;m<=b;)m+=a[e].size,e++;l=a.splice(e-1,a.length)}d(a,c);for(a=r(a,function(a){return{size:a.size,targets:[a.target],align:u(a.align,.5)}});f;){for(e=a.length;e--;)f=a[e],m=(Math.min.apply(0,f.targets)+Math.max.apply(0,f.targets))/2,f.pos=Math.min(Math.max(0,m-f.size* -f.align),b-f.size);e=a.length;for(f=!1;e--;)0a[e].pos&&(a[e-1].size+=a[e].size,a[e-1].targets=a[e-1].targets.concat(a[e].targets),a[e-1].align=.5,a[e-1].pos+a[e-1].size>b&&(a[e-1].pos=b-a[e-1].size),a.splice(e,1),f=!0)}e=0;p(a,function(a){var b=0;p(a.targets,function(){h[e].pos=a.pos+b;b+=h[e].size;e++})});h.push.apply(h,l);d(h,c)};h.prototype.drawDataLabels=function(){function c(a,b){var c=b.filter;return c?(b=c.operator,a=a[c.property],c=c.value,"\x3e"===b&&a>c||"\x3c"=== -b&&a=c||"\x3c\x3d"===b&&a<=c||"\x3d\x3d"===b&&a==c||"\x3d\x3d\x3d"===b&&a===c?!0:!1):!0}var b=this,d=b.options,e=d.dataLabels,f=b.points,h,m,r=b.hasRendered||0,t,w,D=u(e.defer,!!d.animation),q=b.chart.renderer;if(e.enabled||b._hasPointLabels)b.dlProcessOptions&&b.dlProcessOptions(e),w=b.plotGroup("dataLabelsGroup","data-labels",D&&!r?"hidden":"visible",e.zIndex||6),D&&(w.attr({opacity:+r}),r||E(b,"afterAnimate",function(){b.visible&&w.show(!0);w[d.animation?"animate":"attr"]({opacity:1}, -{duration:200})})),m=e,p(f,function(f){var k,p=f.dataLabel,g,x,r=f.connector,z=!p,C;h=f.dlOptions||f.options&&f.options.dataLabels;(k=u(h&&h.enabled,m.enabled)&&!f.isNull)&&(k=!0===c(f,h||e));k&&(e=n(m,h),g=f.getLabelConfig(),C=e[f.formatPrefix+"Format"]||e.format,t=H(C)?l(C,g):(e[f.formatPrefix+"Formatter"]||e.formatter).call(g,e),C=e.style,g=e.rotation,C.color=u(e.color,C.color,b.color,"#000000"),"contrast"===C.color&&(f.contrastColor=q.getContrast(f.color||b.color),C.color=e.inside||0>u(f.labelDistance, -e.distance)||d.stacking?f.contrastColor:"#000000"),d.cursor&&(C.cursor=d.cursor),x={fill:e.backgroundColor,stroke:e.borderColor,"stroke-width":e.borderWidth,r:e.borderRadius||0,rotation:g,padding:e.padding,zIndex:1},a.objectEach(x,function(a,b){void 0===a&&delete x[b]}));!p||k&&H(t)?k&&H(t)&&(p?x.text=t:(p=f.dataLabel=g?q.text(t,0,-9999).addClass("highcharts-data-label"):q.label(t,0,-9999,e.shape,null,null,e.useHTML,null,"data-label"),p.addClass(" highcharts-data-label-color-"+f.colorIndex+" "+(e.className|| -"")+(e.useHTML?"highcharts-tracker":""))),p.attr(x),p.css(C).shadow(e.shadow),p.added||p.add(w),b.alignDataLabel(f,p,e,null,z)):(f.dataLabel=p=p.destroy(),r&&(f.connector=r.destroy()))})};h.prototype.alignDataLabel=function(a,b,d,e,h){var c=this.chart,k=c.inverted,l=u(a.dlBox&&a.dlBox.centerX,a.plotX,-9999),m=u(a.plotY,-9999),n=b.getBBox(),p,q=d.rotation,r=d.align,w=this.visible&&(a.series.forceDL||c.isInsidePlot(l,Math.round(m),k)||e&&c.isInsidePlot(l,k?e.x+1:e.y+e.height-1,k)),z="justify"===u(d.overflow, -"justify");if(w&&(p=d.style.fontSize,p=c.renderer.fontMetrics(p,b).b,e=f({x:k?this.yAxis.len-m:l,y:Math.round(k?this.xAxis.len-l:m),width:0,height:0},e),f(d,{width:n.width,height:n.height}),q?(z=!1,l=c.renderer.rotCorr(p,q),l={x:e.x+d.x+e.width/2+l.x,y:e.y+d.y+{top:0,middle:.5,bottom:1}[d.verticalAlign]*e.height},b[h?"attr":"animate"](l).attr({align:r}),m=(q+720)%360,m=180m,"left"===r?l.y-=m?n.height:0:"center"===r?(l.x-=n.width/2,l.y-=n.height/2):"right"===r&&(l.x-=n.width,l.y-=m?0:n.height)): -(b.align(d,null,e),l=b.alignAttr),z?a.isLabelJustified=this.justifyDataLabel(b,d,l,n,e,h):u(d.crop,!0)&&(w=c.isInsidePlot(l.x,l.y)&&c.isInsidePlot(l.x+n.width,l.y+n.height)),d.shape&&!q))b[h?"attr":"animate"]({anchorX:k?c.plotWidth-a.plotY:a.plotX,anchorY:k?c.plotHeight-a.plotX:a.plotY});w||(b.attr({y:-9999}),b.placed=!1)};h.prototype.justifyDataLabel=function(a,b,d,e,f,h){var c=this.chart,k=b.align,l=b.verticalAlign,m,n,p=a.box?0:a.padding||0;m=d.x+p;0>m&&("right"===k?b.align="left":b.x=-m,n=!0); -m=d.x+e.width-p;m>c.plotWidth&&("left"===k?b.align="right":b.x=c.plotWidth-m,n=!0);m=d.y+p;0>m&&("bottom"===l?b.verticalAlign="top":b.y=-m,n=!0);m=d.y+e.height-p;m>c.plotHeight&&("top"===l?b.verticalAlign="bottom":b.y=c.plotHeight-m,n=!0);n&&(a.placed=!h,a.align(b,null,f));return n};m.pie&&(m.pie.prototype.drawDataLabels=function(){var c=this,b=c.data,d,e=c.chart,f=c.options.dataLabels,l=u(f.connectorPadding,10),m=u(f.connectorWidth,1),n=e.plotWidth,t=e.plotHeight,r,w=c.center,q=w[2]/2,A=w[1],F,G, -g,v,E=[[],[]],L,P,J,M,y=[0,0,0,0];c.visible&&(f.enabled||c._hasPointLabels)&&(p(b,function(a){a.dataLabel&&a.visible&&a.dataLabel.shortened&&(a.dataLabel.attr({width:"auto"}).css({width:"auto",textOverflow:"clip"}),a.dataLabel.shortened=!1)}),h.prototype.drawDataLabels.apply(c),p(b,function(a){a.dataLabel&&a.visible&&(E[a.half].push(a),a.dataLabel._pos=null)}),p(E,function(b,h){var k,m,x=b.length,r=[],z;if(x)for(c.sortByAngle(b,h-.5),0d.bottom-2?k:P,h,d),F._attr={visibility:J,align:g[6]},F._pos={x:L+f.x+({left:l,right:-l}[g[6]]||0),y:P+f.y-10},g.x=L,g.y=P,u(f.crop,!0)&&(G=F.getBBox().width,k=null,L-Gn-l&&(k=Math.round(L+G-n+l),y[1]=Math.max(k,y[1])),0>P-v/2?y[0]=Math.max(Math.round(-P+v/2),y[0]):P+v/2>t&&(y[2]=Math.max(Math.round(P+v/2-t),y[2])),F.sideOverflow=k)}),0===D(y)||this.verifyDataLabelOverflow(y))&&(this.placeDataLabels(), -m&&p(this.points,function(a){var b;r=a.connector;if((F=a.dataLabel)&&F._pos&&a.visible&&0u(this.translatedThreshold,k.yAxis.len)),p=u(d.inside,!!this.options.stacking);l&&(e=n(l),0>e.y&&(e.height+=e.y,e.y=0),l=e.y+e.height-k.yAxis.len,0a+c||e+hb+d||f+lthis.pointCount))},pan:function(a,b){var c=this,d=c.hoverPoints,e;d&&r(d,function(a){a.setState()});r("xy"===b?[1,0]:[1],function(b){b= -c[b?"xAxis":"yAxis"][0];var d=b.horiz,f=a[d?"chartX":"chartY"],d=d?"mouseDownX":"mouseDownY",h=c[d],g=(b.pointRange||0)/2,k=b.getExtremes(),l=b.toValue(h-f,!0)+g,m=b.toValue(h+b.len-f,!0)-g,n=m=l(n.minWidth,0)&&this.chartHeight>=l(n.minHeight,0)}).call(this)&&f.push(a._id)};E.prototype.currentOptions=function(l){function n(e,h,l,d){var c;a.objectEach(e,function(a,e){if(!d&&-1 Date: Mon, 8 Jan 2018 18:12:57 +1300 Subject: [PATCH 071/219] Add a green border around predictions boxes --- app/assets/stylesheets/predictions.sass | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/predictions.sass b/app/assets/stylesheets/predictions.sass index b9496ebc5..00be9af21 100644 --- a/app/assets/stylesheets/predictions.sass +++ b/app/assets/stylesheets/predictions.sass @@ -1,7 +1,7 @@ .predictions .metric height: 180px - border: #000000 2px + border: 1px solid lighten($green, 20%) background: $white margin: 4px strong From 29a9643f00516b15e4e6d59c3e3575bc95a54e84 Mon Sep 17 00:00:00 2001 From: Awesome Code Date: Mon, 8 Jan 2018 23:46:00 +0000 Subject: [PATCH 072/219] trigger awesomecode.io notification From a5fa033f2db1de52c55c6c99c15246f6b08b34f7 Mon Sep 17 00:00:00 2001 From: Awesome Code Date: Mon, 8 Jan 2018 23:46:57 +0000 Subject: [PATCH 073/219] Auto corrected by following EmptyLine --- script/check_contributors_md.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/script/check_contributors_md.rb b/script/check_contributors_md.rb index 3fca50489..4792abbb5 100755 --- a/script/check_contributors_md.rb +++ b/script/check_contributors_md.rb @@ -22,7 +22,6 @@ else abort %( Couldn't determine your GitHub username, and not in a Travis PR build Please set it using - git config --add github.user [username] ) end From 74ff648f90c29cc11a719a459d883a8d3cbc78c6 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 9 Jan 2018 12:51:21 +1300 Subject: [PATCH 074/219] Moved chart includes to application.js, attempting to fix json parse error --- app/assets/javascripts/application.js | 2 ++ app/assets/javascripts/charts.js | 2 -- app/views/crops/show.html.haml | 3 --- app/views/layouts/application.html.haml | 4 +--- config/initializers/assets.rb | 1 - 5 files changed, 3 insertions(+), 9 deletions(-) delete mode 100644 app/assets/javascripts/charts.js diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index fc3369264..e0d7b8463 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -10,6 +10,8 @@ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD // GO AFTER THE REQUIRES BELOW. // +// = require Chart.bundle +// = require chartkick // = require leaflet // = require leaflet.markercluster // = require js-routes diff --git a/app/assets/javascripts/charts.js b/app/assets/javascripts/charts.js deleted file mode 100644 index 1c42da456..000000000 --- a/app/assets/javascripts/charts.js +++ /dev/null @@ -1,2 +0,0 @@ -// = require Chart.bundle -// = require chartkick diff --git a/app/views/crops/show.html.haml b/app/views/crops/show.html.haml index cf401681e..e5e7e7a1a 100644 --- a/app/views/crops/show.html.haml +++ b/app/views/crops/show.html.haml @@ -8,9 +8,6 @@ = tag("meta", property: "og:url", content: request.original_url) = tag("meta", property: "og:site_name", content: ENV['GROWSTUFF_SITE_NAME']) -- content_for :scripts do - = javascript_include_tag "charts" - = render partial: 'approval_status_message', locals: { crop: @crop } - if @crop.approved? diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 40f83db5b..b90c025e7 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,6 +1,6 @@ !!! 5 %html{ lang: "en", prefix: "og: http://ogp.me/ns#" } - = yield :scripts + = javascript_include_tag "application" = render partial: "layouts/meta" %body = render partial: "layouts/header" @@ -27,6 +27,4 @@ Javascripts \================================================== / Placed at the end of the document so the pages load faster - = javascript_include_tag "application" - != Growstuff::Application.config.analytics_code diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 60484d4a4..4b828e80c 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -12,4 +12,3 @@ Rails.application.config.assets.paths << Rails.root.join('node_modules') # application.js, application.css, and all non-JS/CSS in the app/assets # folder are already added. # Rails.application.config.assets.precompile += %w( admin.js admin.css ) -Rails.application.config.assets.precompile += %w(charts.js) From 2c3fe3c2046bc386efb0bddd378eebedf8aba4f2 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 9 Jan 2018 13:18:58 +1300 Subject: [PATCH 075/219] updated prediction spec --- spec/features/crops/crop_detail_page_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/crops/crop_detail_page_spec.rb b/spec/features/crops/crop_detail_page_spec.rb index 05ebdb6e7..29d73a47a 100644 --- a/spec/features/crops/crop_detail_page_spec.rb +++ b/spec/features/crops/crop_detail_page_spec.rb @@ -215,7 +215,7 @@ feature "crop detail page", js: true do end it "predicts lifespan" do - is_expected.to have_text "Median lifespan of #{crop.name} plants is 99 days" + is_expected.to have_text "Median lifespan 99 days" end it "describes annual crops" do From c5c6f7ad47775b875662a9cdb7b78298001b5db3 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 9 Jan 2018 14:31:03 +1300 Subject: [PATCH 076/219] Removed ignore of highcharts --- .overcommit.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.overcommit.yml b/.overcommit.yml index 200a90e42..d7899885f 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -61,7 +61,6 @@ PreCommit: enabled: true exclude: - 'app/assets/**' - - 'app/assets/javascripts/highcharts.js' - 'spec/javascripts/support/vendor/**' - '**/bootstrap*' command: ['./node_modules/.bin/eslint'] From a2e4863f006056530b5eb5b6ac03f38d76e660ac Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 9 Jan 2018 14:37:21 +1300 Subject: [PATCH 077/219] Fixing up prediction specs --- spec/features/crops/crop_detail_page_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/features/crops/crop_detail_page_spec.rb b/spec/features/crops/crop_detail_page_spec.rb index 29d73a47a..e21c5eb47 100644 --- a/spec/features/crops/crop_detail_page_spec.rb +++ b/spec/features/crops/crop_detail_page_spec.rb @@ -181,6 +181,7 @@ feature "crop detail page", js: true do # 10 days to harvest FactoryBot.create(:harvest, harvested_at: 190.days.ago, crop: planting.crop, planting: FactoryBot.create(:planting, planted_at: 200.days.ago, crop: crop)) + planting.crop.update_medians end it "predicts harvest" do is_expected.to have_text("First harvest expected 20 days after planting") @@ -215,7 +216,8 @@ feature "crop detail page", js: true do end it "predicts lifespan" do - is_expected.to have_text "Median lifespan 99 days" + is_expected.to have_text "Median lifespan" + is_expected.to have_text "99 days" end it "describes annual crops" do From 3ece5843909373c39317fd16f6a44213e699ce79 Mon Sep 17 00:00:00 2001 From: Awesome Code Date: Tue, 9 Jan 2018 01:59:43 +0000 Subject: [PATCH 078/219] Auto corrected by following FactoryBot/DynamicAttributeDefinedStatically --- spec/factories/harvests.rb | 2 +- spec/factories/member.rb | 2 +- spec/factories/planting.rb | 6 +++--- spec/factories/post.rb | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/factories/harvests.rb b/spec/factories/harvests.rb index c707a2e2c..ae3144a1e 100644 --- a/spec/factories/harvests.rb +++ b/spec/factories/harvests.rb @@ -6,7 +6,7 @@ FactoryBot.define do plant_part planting nil owner { planting.present? ? planting.owner : FactoryBot.create(:member) } - harvested_at Time.zone.local(2015, 9, 17) + harvested_at { Time.zone.local(2015, 9, 17) } quantity "3" unit "individual" weight_quantity 6 diff --git a/spec/factories/member.rb b/spec/factories/member.rb index 619e7d4f2..016b12263 100644 --- a/spec/factories/member.rb +++ b/spec/factories/member.rb @@ -7,7 +7,7 @@ FactoryBot.define do password 'password1' email { generate(:email) } tos_agreement true - confirmed_at Time.now + confirmed_at { Time.now } show_email false bio 'I love seeds' diff --git a/spec/factories/planting.rb b/spec/factories/planting.rb index 2bcfac397..5b7311025 100644 --- a/spec/factories/planting.rb +++ b/spec/factories/planting.rb @@ -3,7 +3,7 @@ FactoryBot.define do owner garden { FactoryBot.create :garden, owner: owner } crop - planted_at Time.zone.local(2014, 7, 30) + planted_at { Time.zone.local(2014, 7, 30) } quantity 33 description "This is a *really* good plant." @@ -33,8 +33,8 @@ FactoryBot.define do factory :finished_planting do finished true - planted_at Time.zone.local(2014, 7, 30) - finished_at Time.zone.local(2014, 8, 30) + planted_at { Time.zone.local(2014, 7, 30) } + finished_at { Time.zone.local(2014, 8, 30) } end end end diff --git a/spec/factories/post.rb b/spec/factories/post.rb index d53f33100..da6390ef8 100644 --- a/spec/factories/post.rb +++ b/spec/factories/post.rb @@ -3,7 +3,7 @@ FactoryBot.define do subject "A Post" body "This is some text." author - created_at Time.now + created_at { Time.now } # Markdown is allowed in posts factory :markdown_post do From e46f530dc3d6e6ad0e1884e767e9cbed15b5dbc4 Mon Sep 17 00:00:00 2001 From: Awesome Code Date: Tue, 9 Jan 2018 02:00:40 +0000 Subject: [PATCH 079/219] Auto corrected by following RSpec/EmptyLineAfterFinalLet --- spec/controllers/account_types_controller_spec.rb | 1 + spec/controllers/comments_controller_spec.rb | 11 +++++++++++ spec/controllers/gardens_controller_spec.rb | 2 ++ spec/controllers/harvests_controller_spec.rb | 4 ++++ spec/controllers/likes_controller_spec.rb | 1 + .../controllers/photo_associations_controller_spec.rb | 1 + spec/controllers/photos_controller_spec.rb | 3 +++ spec/controllers/plantings_controller_spec.rb | 5 +++++ spec/controllers/shop_controller_spec.rb | 1 + spec/features/crops/crop_detail_page_spec.rb | 4 ++++ spec/features/crops/requested_crops_spec.rb | 1 + spec/features/gardens_spec.rb | 1 + spec/features/members/deletion_spec.rb | 1 + spec/features/plantings/planting_a_crop_spec.rb | 1 + spec/features/seeds/seed_photos.rb | 2 ++ spec/features/signout_spec.rb | 1 + spec/models/crop_spec.rb | 8 ++++++++ spec/models/member_spec.rb | 4 ++++ spec/models/photo_spec.rb | 1 + spec/models/planting_spec.rb | 11 +++++++++++ spec/models/post_spec.rb | 1 + spec/models/role_spec.rb | 2 ++ spec/models/seed_spec.rb | 1 + spec/views/crops/_planting_advice.html.haml_spec.rb | 1 + spec/views/home/_crops.html.haml_spec.rb | 1 + spec/views/members/index.html.haml_spec.rb | 1 + spec/views/plantings/index.html.haml_spec.rb | 1 + spec/views/plantings/show.html.haml_spec.rb | 1 + spec/views/posts/show.html.haml_spec.rb | 9 +++++++++ 29 files changed, 82 insertions(+) diff --git a/spec/controllers/account_types_controller_spec.rb b/spec/controllers/account_types_controller_spec.rb index 37dbfa706..010c8bf1f 100644 --- a/spec/controllers/account_types_controller_spec.rb +++ b/spec/controllers/account_types_controller_spec.rb @@ -21,6 +21,7 @@ describe AccountTypesController do describe '#index' do let!(:aaa) { FactoryBot.create :account_type, name: 'aaa' } let!(:zzz) { FactoryBot.create :account_type, name: 'zzz' } + before { get :index } it { is_expected.to be_success } it { expect(assigns[:account_types].first).to eql(aaa) } diff --git a/spec/controllers/comments_controller_spec.rb b/spec/controllers/comments_controller_spec.rb index 0e986cc93..2d265de5f 100644 --- a/spec/controllers/comments_controller_spec.rb +++ b/spec/controllers/comments_controller_spec.rb @@ -3,6 +3,7 @@ require 'rails_helper' describe CommentsController do subject { response } let(:member) { FactoryBot.create(:member) } + before(:each) do sign_in member controller.stub(:current_member) { member } @@ -16,6 +17,7 @@ describe CommentsController do describe "GET RSS feed" do let!(:first_comment) { FactoryBot.create :comment, created_at: 10.days.ago } let!(:last_comment) { FactoryBot.create :comment, created_at: 4.minutes.ago } + describe "returns an RSS feed" do before { get :index, format: "rss" } it { is_expected.to be_success } @@ -36,6 +38,7 @@ describe CommentsController do end let(:old_comment) { FactoryBot.create(:comment, post: post) } + it "assigns the old comments as @comments" do assigns(:comments).should eq [old_comment] end @@ -49,11 +52,13 @@ describe CommentsController do describe "GET edit" do let(:post) { FactoryBot.create(:post) } + before { get :edit, id: comment.to_param } describe "my comment" do let!(:comment) { FactoryBot.create :comment, author: member, post: post } let!(:old_comment) { FactoryBot.create(:comment, post: post, created_at: Time.zone.yesterday) } + it "assigns previous comments as @comments" do expect(assigns(:comments)).to eq([comment, old_comment]) end @@ -61,6 +66,7 @@ describe CommentsController do describe "not my comment" do let(:comment) { FactoryBot.create :comment, post: post } + it { expect(response).not_to be_success } end end @@ -70,12 +76,14 @@ describe CommentsController do describe "my comment" do let(:comment) { FactoryBot.create :comment, author: member } + it "redirects to the comment's post" do expect(response).to redirect_to(comment.post) end end describe "not my comment" do let(:comment) { FactoryBot.create :comment } + it { expect(response).not_to be_success } end describe "attempting to change post_id" do @@ -83,6 +91,7 @@ describe CommentsController do let(:other_post) { FactoryBot.create :post, subject: 'the other post' } let(:valid_attributes) { { post_id: other_post.id, body: "kōrero" } } let(:comment) { FactoryBot.create :comment, author: member, post: post } + it "does not change post_id" do comment.reload expect(comment.post_id).to eq(post.id) @@ -95,6 +104,7 @@ describe CommentsController do describe "my comment" do let(:comment) { FactoryBot.create :comment, author: member } + it "redirects to the post the comment was on" do expect(response).to redirect_to(comment.post) end @@ -102,6 +112,7 @@ describe CommentsController do describe "not my comment" do let(:comment) { FactoryBot.create :comment } + it { expect(response).not_to be_success } end end diff --git a/spec/controllers/gardens_controller_spec.rb b/spec/controllers/gardens_controller_spec.rb index b0ccae0b8..9253b5ddb 100644 --- a/spec/controllers/gardens_controller_spec.rb +++ b/spec/controllers/gardens_controller_spec.rb @@ -6,6 +6,7 @@ RSpec.describe GardensController, type: :controller do context "when not signed in" do let(:garden) { double('garden') } + describe 'GET new' do before { get :new, id: garden.to_param } it { expect(response).to redirect_to(new_member_session_path) } @@ -45,6 +46,7 @@ RSpec.describe GardensController, type: :controller do describe "for another member's garden" do let(:not_my_garden) { double('garden') } + before do expect(Garden).to receive(:find).and_return(:not_my_garden) expect(not_my_garden).not_to receive(:save) diff --git a/spec/controllers/harvests_controller_spec.rb b/spec/controllers/harvests_controller_spec.rb index 8a2edb56b..705f954b5 100644 --- a/spec/controllers/harvests_controller_spec.rb +++ b/spec/controllers/harvests_controller_spec.rb @@ -45,6 +45,7 @@ describe HarvestsController do describe "GET show" do let(:harvest) { Harvest.create! valid_attributes } + describe "assigns the requested harvest as @harvest" do before { get :show, id: harvest.to_param } it { expect(assigns(:harvest)).to eq(harvest) } @@ -65,6 +66,7 @@ describe HarvestsController do describe "GET edit" do let(:harvest) { Harvest.create! valid_attributes } + describe "assigns the requested harvest as @harvest" do before { get :edit, id: harvest.to_param } it { expect(assigns(:harvest)).to eq(harvest) } @@ -92,6 +94,7 @@ describe HarvestsController do describe "links to planting" do let(:planting) { FactoryBot.create(:planting, owner_id: member.id, garden: member.gardens.first) } + before { post :create, harvest: valid_attributes.merge(planting_id: planting.id) } it { expect(Harvest.last.planting.id).to eq(planting.id) } end @@ -170,6 +173,7 @@ describe HarvestsController do describe "not my planting" do let(:not_my_planting) { FactoryBot.create(:planting) } let(:harvest) { FactoryBot.create(:harvest) } + describe "does not save planting_id" do before do put :update, id: harvest.to_param, diff --git a/spec/controllers/likes_controller_spec.rb b/spec/controllers/likes_controller_spec.rb index 6f43760c1..a80fc0d56 100644 --- a/spec/controllers/likes_controller_spec.rb +++ b/spec/controllers/likes_controller_spec.rb @@ -35,6 +35,7 @@ describe LikesController do describe "Deleting someone else's like" do let(:like) { FactoryBot.create :like } + it { expect(response.code).to eq('403') } it { JSON.parse(response.body)["error"] == "Unable to like" } end diff --git a/spec/controllers/photo_associations_controller_spec.rb b/spec/controllers/photo_associations_controller_spec.rb index 42e0ccc37..75a04eedd 100644 --- a/spec/controllers/photo_associations_controller_spec.rb +++ b/spec/controllers/photo_associations_controller_spec.rb @@ -26,6 +26,7 @@ describe PhotoAssociationsController do describe "another member's harvest from another member's photo" do let(:harvest) { FactoryBot.create :harvest } let(:photo) { FactoryBot.create :photo } + it do expect do begin diff --git a/spec/controllers/photos_controller_spec.rb b/spec/controllers/photos_controller_spec.rb index 3c4f9dacb..76869ebf3 100644 --- a/spec/controllers/photos_controller_spec.rb +++ b/spec/controllers/photos_controller_spec.rb @@ -73,6 +73,7 @@ describe PhotosController do let(:planting) { FactoryBot.create(:planting, garden: garden, owner: member) } let(:harvest) { FactoryBot.create(:harvest, owner: member) } let(:photo) { FactoryBot.create(:photo, owner: member) } + describe "with valid params" do before { controller.stub(:current_member) { member } } it "attaches the photo to a planting" do @@ -113,6 +114,7 @@ describe PhotosController do describe "for the second time" do let(:planting) { FactoryBot.create :planting, owner: member } + it "does not add a photo twice" do expect do post :create, photo: { flickr_photo_id: 1 }, id: planting.id, type: 'planting' @@ -142,6 +144,7 @@ describe PhotosController do describe "with mismatched owners" do let(:photo) { FactoryBot.create(:photo) } + it "does not create the planting/photo link" do # members will be auto-created, and different another_planting = FactoryBot.create(:planting) diff --git a/spec/controllers/plantings_controller_spec.rb b/spec/controllers/plantings_controller_spec.rb index dd1340d44..abe5ea626 100644 --- a/spec/controllers/plantings_controller_spec.rb +++ b/spec/controllers/plantings_controller_spec.rb @@ -39,6 +39,7 @@ describe PlantingsController do describe "GET new" do describe "picks up crop from params" do let(:crop) { FactoryBot.create(:crop) } + before { get :new, crop_id: crop.id } it { expect(assigns(:crop)).to eq(crop) } end @@ -50,6 +51,7 @@ describe PlantingsController do describe "picks up member's garden from params" do let(:garden) { FactoryBot.create(:garden, owner: member) } + before { get :new, garden_id: garden.id } it { expect(assigns(:garden)).to eq(garden) } end @@ -57,6 +59,7 @@ describe PlantingsController do describe "Doesn't display another member's garden on planting form" do let(:another_member) { FactoryBot.create(:member) } # over-riding member from login_member() let(:garden) { FactoryBot.create(:garden, owner: another_member) } + before { get :new, garden_id: garden.id } it { expect(assigns(:garden)).not_to eq(garden) } end @@ -64,6 +67,7 @@ describe PlantingsController do describe "Doesn't display un-approved crops on planting form" do let(:crop) { FactoryBot.create(:crop, approval_status: 'pending') } let!(:garden) { FactoryBot.create(:garden, owner: member) } + before { get :new, crop_id: crop.id } it { expect(assigns(:crop)).not_to eq(crop) } end @@ -71,6 +75,7 @@ describe PlantingsController do describe "Doesn't display rejected crops on planting form" do let(:crop) { FactoryBot.create(:crop, approval_status: 'rejected', reason_for_rejection: 'nope') } let!(:garden) { FactoryBot.create(:garden, owner: member) } + before { get :new, crop_id: crop.id } it { expect(assigns(:crop)).not_to eq(crop) } end diff --git a/spec/controllers/shop_controller_spec.rb b/spec/controllers/shop_controller_spec.rb index c25039d2d..2fc6b77f7 100644 --- a/spec/controllers/shop_controller_spec.rb +++ b/spec/controllers/shop_controller_spec.rb @@ -24,6 +24,7 @@ describe ShopController do describe "assigns @order as current_order if there is one" do let(:member) { FactoryBot.create(:member) } let!(:order) { FactoryBot.create(:order, member: member) } + before do sign_in member get :index, {} diff --git a/spec/features/crops/crop_detail_page_spec.rb b/spec/features/crops/crop_detail_page_spec.rb index 05ebdb6e7..7e927756d 100644 --- a/spec/features/crops/crop_detail_page_spec.rb +++ b/spec/features/crops/crop_detail_page_spec.rb @@ -204,6 +204,7 @@ feature "crop detail page", js: true do planted_at: 100.days.ago, finished_at: 1.day.ago) end + context 'crop is an annual' do let(:crop) { FactoryBot.create(:annual_crop) } @@ -256,16 +257,19 @@ feature "crop detail page", js: true do before { visit crop_path(crop) } context 'crop is an annual' do let(:crop) { FactoryBot.create :annual_crop } + it { expect(page).to have_text 'annual crop (living and reproducing in a single year or less)' } it { expect(page).not_to have_text 'perennial crop (living more than two years)' } end context 'crop is perennial' do let(:crop) { FactoryBot.create :perennial_crop } + it { expect(page).to have_text 'perennial crop (living more than two years)' } it { expect(page).not_to have_text 'annual crop (living and reproducing in a single year or less)' } end context 'crop perennial value is null' do let(:crop) { FactoryBot.create :crop, perennial: nil } + it { expect(page).not_to have_text 'perennial crop (living more than two years)' } it { expect(page).not_to have_text 'annual crop (living and reproducing in a single year or less)' } end diff --git a/spec/features/crops/requested_crops_spec.rb b/spec/features/crops/requested_crops_spec.rb index 6bfb8220c..15226efe4 100644 --- a/spec/features/crops/requested_crops_spec.rb +++ b/spec/features/crops/requested_crops_spec.rb @@ -3,6 +3,7 @@ require 'rails_helper' feature "Crop - " do let(:member) { create :member } let!(:requested_crop) { create :crop, requester: member, approval_status: 'pending', name: 'puha for dinner' } + background do login_as member visit requested_crops_path diff --git a/spec/features/gardens_spec.rb b/spec/features/gardens_spec.rb index a299a0197..0954d35eb 100644 --- a/spec/features/gardens_spec.rb +++ b/spec/features/gardens_spec.rb @@ -88,6 +88,7 @@ feature "Planting a crop", js: true do describe "Making a planting inactive from garden show" do let(:path) { garden_path garden } let(:link_text) { "Mark as finished" } + it_behaves_like "append date" end diff --git a/spec/features/members/deletion_spec.rb b/spec/features/members/deletion_spec.rb index 3009f51be..793a6ada5 100644 --- a/spec/features/members/deletion_spec.rb +++ b/spec/features/members/deletion_spec.rb @@ -12,6 +12,7 @@ feature "member deletion" do let!(:secondgarden) { FactoryBot.create(:garden, owner: member) } let!(:order) { FactoryBot.create(:order, member: member, completed_at: Time.zone.now) } let(:admin) { FactoryBot.create(:admin_member) } + background do login_as(member) visit member_path(other_member) diff --git a/spec/features/plantings/planting_a_crop_spec.rb b/spec/features/plantings/planting_a_crop_spec.rb index 0c984837b..51ff69be2 100644 --- a/spec/features/plantings/planting_a_crop_spec.rb +++ b/spec/features/plantings/planting_a_crop_spec.rb @@ -270,6 +270,7 @@ feature "Planting a crop", :js, :elasticsearch do describe "Marking a planting as finished from the show page" do let(:path) { planting_path(planting) } let(:link_text) { "Mark as finished" } + it_behaves_like "append date" end diff --git a/spec/features/seeds/seed_photos.rb b/spec/features/seeds/seed_photos.rb index 635b4dc4d..3d57f2cfe 100644 --- a/spec/features/seeds/seed_photos.rb +++ b/spec/features/seeds/seed_photos.rb @@ -19,12 +19,14 @@ feature "Seeds", :js do context 'has one photo' do before { seed.photos = [photo] } let!(:photo) { FactoryBot.create :photo, title: 'hello photo' } + it { is_expected.to have_xpath("//img[contains(@src,'#{photo.thumbnail_url}')]") } it { is_expected.to have_xpath("//a[contains(@href,'#{photo_path(photo)}')]") } end context 'has 50 photos' do before { seed.photos = photos } let!(:photos) { FactoryBot.create_list :photo, 50 } + it "shows newest photo" do is_expected.to have_xpath("//img[contains(@src,'#{photos.last.thumbnail_url}')]") end diff --git a/spec/features/signout_spec.rb b/spec/features/signout_spec.rb index a6402df2c..35cd8af9b 100644 --- a/spec/features/signout_spec.rb +++ b/spec/features/signout_spec.rb @@ -30,6 +30,7 @@ feature "signout" do end let(:path) {} + describe 'after signout, redirect to signin page if page needs authentication' do include_examples "sign-in redirects", "/plantings/new" include_examples "sign-in redirects", "/harvests/new" diff --git a/spec/models/crop_spec.rb b/spec/models/crop_spec.rb index f89cc3377..515d0160c 100644 --- a/spec/models/crop_spec.rb +++ b/spec/models/crop_spec.rb @@ -82,36 +82,43 @@ describe Crop do context 'not a url' do let(:wikipedia_url) { 'this is not valid' } + it { expect(subject).not_to be_valid } end context 'http url' do let(:wikipedia_url) { 'http://en.wikipedia.org/wiki/SomePage' } + it { expect(subject).to be_valid } end context 'with ssl' do let(:wikipedia_url) { 'https://en.wikipedia.org/wiki/SomePage' } + it { expect(subject).to be_valid } end context 'with utf8 macrons' do let(:wikipedia_url) { 'https://en.wikipedia.org/wiki/Māori' } + it { expect(subject).to be_valid } end context 'urlencoded' do let(:wikipedia_url) { 'https://en.wikipedia.org/wiki/M%C4%81ori' } + it { expect(subject).to be_valid } end context 'with new lines in url' do let(:wikipedia_url) { 'http://en.wikipedia.org/wiki/SomePage\n\nBrendaRocks' } + it { expect(subject).not_to be_valid } end context "with script tags in url" do let(:wikipedia_url) { 'http://en.wikipedia.org/wiki/SomePage' } + it { expect(subject).not_to be_valid } end end @@ -330,6 +337,7 @@ describe Crop do let(:h2) { FactoryBot.create(:harvest, crop: maize, plant_part: pp2) } let!(:crop) { FactoryBot.create(:crop) } let!(:harvest) { FactoryBot.create(:harvest, crop: crop) } + it "has harvests" do expect(crop.harvests).to eq [harvest] end diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 1911ae8da..cf7b81486 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -432,6 +432,7 @@ describe 'member' do context 'member deleted' do let(:member) { FactoryBot.create(:member) } + context 'queries a scope' do before { member.destroy } it { expect(Member.all).not_to include(member) } @@ -450,10 +451,12 @@ describe 'member' do context "deleted admin member" do let(:member) { FactoryBot.create(:admin_member) } + before { member.destroy } context 'crop creator' do let!(:crop) { FactoryBot.create(:crop, creator: member) } + it "leaves crops behind, reassigned to cropbot" do expect(Crop.all).to include(crop) end @@ -461,6 +464,7 @@ describe 'member' do context 'forum owners' do let!(:forum) { FactoryBot.create(:forum, owner: member) } + it "leaves forums behind, reassigned to ex_admin" do expect(forum.owner).to eq(member) end diff --git a/spec/models/photo_spec.rb b/spec/models/photo_spec.rb index e220bfe59..c971ce2fd 100644 --- a/spec/models/photo_spec.rb +++ b/spec/models/photo_spec.rb @@ -3,6 +3,7 @@ require 'rails_helper' describe Photo do let(:photo) { FactoryBot.create(:photo, owner: member) } let(:member) { FactoryBot.create(:member) } + describe 'add/delete functionality' do let(:planting) { FactoryBot.create(:planting) } let(:harvest) { FactoryBot.create(:harvest) } diff --git a/spec/models/planting_spec.rb b/spec/models/planting_spec.rb index 48a035bb4..60a95c245 100644 --- a/spec/models/planting_spec.rb +++ b/spec/models/planting_spec.rb @@ -13,6 +13,7 @@ describe Planting do context 'no predications data yet' do describe 'planting planted, not finished' do let(:planting) { FactoryBot.create :planting, planted_at: 30.days.ago, finished_at: nil, finished: false } + it { expect(planting.crop.median_lifespan).to eq(nil) } it { expect(planting.expected_lifespan).to eq(nil) } it { expect(planting.days_since_planted).to eq(30) } @@ -20,6 +21,7 @@ describe Planting do end describe 'planting not planted yet' do let(:planting) { FactoryBot.create :planting, planted_at: nil, finished_at: nil, finished: false } + it { expect(planting.crop.median_lifespan).to eq(nil) } it { expect(planting.expected_lifespan).to eq(nil) } it { expect(planting.days_since_planted).to eq(nil) } @@ -27,6 +29,7 @@ describe Planting do end describe 'planting finished, no planted_at' do let(:planting) { FactoryBot.create :planting, planted_at: nil, finished_at: 1.day.ago, finished: true } + it { expect(planting.crop.median_lifespan).to eq(nil) } it { expect(planting.expected_lifespan).to eq(nil) } it { expect(planting.days_since_planted).to eq(nil) } @@ -34,6 +37,7 @@ describe Planting do end describe 'planting all finished' do let(:planting) { FactoryBot.create :planting, planted_at: 30.days.ago, finished_at: 1.day.ago, finished: true } + it { expect(planting.crop.median_lifespan).to eq(nil) } it { expect(planting.expected_lifespan).to eq(29) } it { expect(planting.days_since_planted).to eq(30) } @@ -63,16 +67,19 @@ describe Planting do describe 'planting not planted yet' do let(:planting) { FactoryBot.create :planting, planted_at: nil, finished_at: nil } + it { expect(planting.percentage_grown).to eq nil } end describe 'planting finished 10 days, but was never planted' do let(:planting) { FactoryBot.create :planting, planted_at: nil, finished_at: 10.days.ago } + it { expect(planting.percentage_grown).to eq nil } end describe 'planted 30 days ago, finished 10 days ago' do let(:planting) { FactoryBot.create :planting, planted_at: 30.days.ago, finished_at: 10.days.ago } + it { expect(planting.days_since_planted).to eq 30 } it { expect(planting.percentage_grown).to eq 100 } end @@ -82,6 +89,7 @@ describe Planting do describe 'planting first harvest preductions' do context 'no data' do let(:planting) { FactoryBot.create :planting } + it { expect(planting.crop.median_days_to_first_harvest).to eq(nil) } it { expect(planting.crop.median_days_to_last_harvest).to eq(nil) } it { expect(planting.days_to_first_harvest).to eq(nil) } @@ -114,11 +122,13 @@ describe Planting do planting.crop.update_harvest_medians end let(:planting) { FactoryBot.create :planting } + it { expect(planting.days_to_first_harvest).to eq(nil) } it { expect(planting.days_to_last_harvest).to eq(nil) } end describe 'planting has first harvest' do let(:planting) { FactoryBot.create :planting, planted_at: 100.days.ago } + before do FactoryBot.create(:harvest, planting: planting, @@ -134,6 +144,7 @@ describe Planting do end describe 'planting has last harvest' do let(:planting) { FactoryBot.create :planting, planted_at: 100.days.ago, finished_at: 1.day.ago, finished: true } + before do FactoryBot.create :harvest, planting: planting, crop: planting.crop, harvested_at: 90.days.ago FactoryBot.create :harvest, planting: planting, crop: planting.crop, harvested_at: 10.days.ago diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index cb8cbcc91..062aba487 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -2,6 +2,7 @@ require 'rails_helper' describe Post do let(:member) { FactoryBot.create(:member) } + it_behaves_like "it is likeable" it "should have a slug" do diff --git a/spec/models/role_spec.rb b/spec/models/role_spec.rb index 3016591b7..a7cfbecd3 100644 --- a/spec/models/role_spec.rb +++ b/spec/models/role_spec.rb @@ -19,6 +19,7 @@ describe Role do describe '.crop_wranglers' do let!(:crop_wranglers) { FactoryBot.create_list(:crop_wrangling_member, 3) } + it 'return the crop wranglers that are members of that role' do expect(Role.crop_wranglers).to match_array(crop_wranglers) end @@ -26,6 +27,7 @@ describe Role do describe '.admins' do let!(:admins) { FactoryBot.create_list(:admin_member, 3) } + it 'return the members that have the role of admin' do expect(Role.admins).to match_array(admins) end diff --git a/spec/models/seed_spec.rb b/spec/models/seed_spec.rb index c1cd95cbd..e45d0e240 100644 --- a/spec/models/seed_spec.rb +++ b/spec/models/seed_spec.rb @@ -151,6 +151,7 @@ describe Seed do context 'photos' do let(:seed) { FactoryBot.create :seed } + before { seed.photos << FactoryBot.create(:photo) } it 'is found in has_photos scope' do Seed.has_photos.should include(seed) diff --git a/spec/views/crops/_planting_advice.html.haml_spec.rb b/spec/views/crops/_planting_advice.html.haml_spec.rb index c7a4719b5..38499e2d9 100644 --- a/spec/views/crops/_planting_advice.html.haml_spec.rb +++ b/spec/views/crops/_planting_advice.html.haml_spec.rb @@ -2,6 +2,7 @@ require 'rails_helper' describe "crops/_planting_advice" do let(:planting) { FactoryBot.create(:planting) } + subject { rendered } shared_examples "render planting_advice" do diff --git a/spec/views/home/_crops.html.haml_spec.rb b/spec/views/home/_crops.html.haml_spec.rb index 254fcb2f6..3ed655438 100644 --- a/spec/views/home/_crops.html.haml_spec.rb +++ b/spec/views/home/_crops.html.haml_spec.rb @@ -4,6 +4,7 @@ describe 'home/_crops.html.haml', type: "view" do let!(:crop) { FactoryBot.create(:crop, plantings: FactoryBot.create_list(:planting, 3)) } let!(:photo) { FactoryBot.create(:photo, plantings: [crop.plantings.first]) } let(:planting) { crop.plantings.first } + before(:each) { render } it 'shows crops section' do assert_select 'h2', text: 'Some of our crops' diff --git a/spec/views/members/index.html.haml_spec.rb b/spec/views/members/index.html.haml_spec.rb index 81e8a6670..87dd3a285 100644 --- a/spec/views/members/index.html.haml_spec.rb +++ b/spec/views/members/index.html.haml_spec.rb @@ -2,6 +2,7 @@ require 'rails_helper' describe "members/index" do let(:member) { FactoryBot.create(:london_member) } + before(:each) do controller.stub(:current_user) { nil } page = 1 diff --git a/spec/views/plantings/index.html.haml_spec.rb b/spec/views/plantings/index.html.haml_spec.rb index f682d62e9..a3a713100 100644 --- a/spec/views/plantings/index.html.haml_spec.rb +++ b/spec/views/plantings/index.html.haml_spec.rb @@ -5,6 +5,7 @@ describe "plantings/index" do let(:garden) { FactoryBot.create(:garden, owner: member) } let(:tomato) { FactoryBot.create(:tomato) } let(:maize) { FactoryBot.create(:maize) } + before(:each) do controller.stub(:current_user) { nil } page = 1 diff --git a/spec/views/plantings/show.html.haml_spec.rb b/spec/views/plantings/show.html.haml_spec.rb index 040a0f6b0..899724ac7 100644 --- a/spec/views/plantings/show.html.haml_spec.rb +++ b/spec/views/plantings/show.html.haml_spec.rb @@ -28,6 +28,7 @@ describe "plantings/show" do context 'planted from' do let(:planting) { FactoryBot.create(:cutting_planting) } + it "shows planted_from" do render rendered.should have_content 'Planted from:' diff --git a/spec/views/posts/show.html.haml_spec.rb b/spec/views/posts/show.html.haml_spec.rb index 169e90da0..273f400cc 100644 --- a/spec/views/posts/show.html.haml_spec.rb +++ b/spec/views/posts/show.html.haml_spec.rb @@ -25,25 +25,30 @@ describe "posts/show" do describe "should parse markdown into html" do let(:post) { FactoryBot.create(:markdown_post, author: author) } + it { assert_select "strong", "strong" } end describe "shouldn't let html through in body" do let(:post) { FactoryBot.create(:post, author: author, body: 'EVIL') } + it { is_expected.to have_content('EVIL') } it { is_expected.not_to have_link("http://evil.com") } end describe 'script tag in post body' do let(:post) { FactoryBot.create(:post, author: author, body: "") } + it { is_expected.not_to have_selector('script') } end describe 'script tag in post title' do let(:post) { FactoryBot.create(:post, author: author, subject: "") } + it { is_expected.not_to have_selector('script') } end describe 'has an anchor to the comments' do let(:post) { FactoryBot.create(:post, author: author) } + it { is_expected.to have_selector('a[name=comments]') } end end @@ -51,6 +56,7 @@ describe "posts/show" do context "when there is one comment" do let(:post) { FactoryBot.create(:html_post, author: author) } let!(:comment) { FactoryBot.create(:comment, post: post) } + before(:each) do @comments = post.comments render @@ -71,6 +77,7 @@ describe "posts/show" do context "when there is more than one comment" do let(:post) { FactoryBot.create(:html_post, author: author) } + before(:each) do @comment1 = FactoryBot.create(:comment, post: post, body: "F1rst!!!", created_at: Date.new(2010, 5, 17)) @@ -90,6 +97,7 @@ describe "posts/show" do context "forum post" do let(:post) { FactoryBot.create(:forum_post, author: author) } + before { render } it "shows forum name" do is_expected.to have_content "in #{post.forum.name}" @@ -98,6 +106,7 @@ describe "posts/show" do context "signed in" do let(:post) { FactoryBot.create(:post, author: author) } + before(:each) do sign_in author controller.stub(:current_user) { author } From dc86404b6f3b44109aad1a9744d03bb5e6f2283a Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 9 Jan 2018 15:53:40 +1300 Subject: [PATCH 080/219] Fix predictions, it wasn't showing nil --- app/views/crops/_predictions.html.haml | 40 +++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/app/views/crops/_predictions.html.haml b/app/views/crops/_predictions.html.haml index 343f4f6bd..2a647d350 100644 --- a/app/views/crops/_predictions.html.haml +++ b/app/views/crops/_predictions.html.haml @@ -1,5 +1,5 @@ -- unless crop.perennial.nil? - .predictions +.predictions + - unless crop.perennial.nil? .row %p #{crop.name} is @@ -12,23 +12,23 @@ an annual crop (living and reproducing in a single year or less) - .row - - if crop.annual? && crop.median_lifespan.present? - .metric.col-md-3.col-xs-5 - %h3 Median lifespan - %strong= crop.median_lifespan - %span days + .row + - if crop.annual? && crop.median_lifespan.present? + .metric.col-md-3.col-xs-5 + %h3 Median lifespan + %strong= crop.median_lifespan + %span days - - if crop.median_days_to_first_harvest.present? - .metric.col-md-3.col-xs-5 - %h3 First harvest expected - %strong= crop.median_days_to_first_harvest - %span days - after planting + - if crop.median_days_to_first_harvest.present? + .metric.col-md-3.col-xs-5 + %h3 First harvest expected + %strong= crop.median_days_to_first_harvest + %span days + after planting - - if crop.annual? && crop.median_days_to_last_harvest.present? - .metric.col-md-3.col-xs-5 - %h3 Last harvest expected - %strong= crop.median_days_to_last_harvest - %span days - after planting + - if crop.annual? && crop.median_days_to_last_harvest.present? + .metric.col-md-3.col-xs-5 + %h3 Last harvest expected + %strong= crop.median_days_to_last_harvest + %span days + after planting From 15594c020977077cc053526eb3ab474654b86fd1 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 11 Jan 2018 08:26:59 +1300 Subject: [PATCH 081/219] Wrapped thre crops show js in a document.ready --- app/assets/javascripts/crops.js.erb | 31 ++++++++++++++++------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/crops.js.erb b/app/assets/javascripts/crops.js.erb index 9a60dd60c..4e2a67e82 100644 --- a/app/assets/javascripts/crops.js.erb +++ b/app/assets/javascripts/crops.js.erb @@ -1,14 +1,3 @@ -if (document.getElementById("cropmap") !== null) { - mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>"; - mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>"; - mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token; - - L.Icon.Default.imagePath = '/assets' - - cropmap = L.map('cropmap').setView([0.0, -0.0], 2); - showCropMap(cropmap); -} - function showCropMap(cropmap) { L.tileLayer(mapbox_base_url, { @@ -49,6 +38,20 @@ function showCropMap(cropmap) { cropmap.addLayer(markers); } -$('.btn.toggle.crop-hierarchy').click(function () { - $('.toggle.crop-hierarchy').toggleClass('hide'); -}); +$(document).ready(function() { + + if (document.getElementById("cropmap") !== null) { + mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>"; + mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>"; + mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token; + + L.Icon.Default.imagePath = '/assets' + + cropmap = L.map('cropmap').setView([0.0, -0.0], 2); + showCropMap(cropmap); + } + + $('.btn.toggle.crop-hierarchy').click(function () { + $('.toggle.crop-hierarchy').toggleClass('hide'); + }); +}); \ No newline at end of file From ad6601b356d0442f5ee605e591ab60332337322c Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 11 Jan 2018 08:29:20 +1300 Subject: [PATCH 082/219] Add planted_from chart --- app/controllers/crops_controller.rb | 8 +++++ app/views/crops/_predictions.html.haml | 21 ++++++------- app/views/crops/show.html.haml | 41 ++++++++++++++++---------- config/routes.rb | 1 + 4 files changed, 45 insertions(+), 26 deletions(-) diff --git a/app/controllers/crops_controller.rb b/app/controllers/crops_controller.rb index b62177e5d..371e70726 100644 --- a/app/controllers/crops_controller.rb +++ b/app/controllers/crops_controller.rb @@ -110,6 +110,14 @@ class CropsController < ApplicationController .group(:sunniness).count(:id) end + def planted_from + @crop = Crop.find(params[:crop_id]) + render json: Planting.where(crop: @crop) + .where.not(planted_from: nil) + .where.not(planted_from: '') + .group(:planted_from).count(:id) + end + private def notifier diff --git a/app/views/crops/_predictions.html.haml b/app/views/crops/_predictions.html.haml index 2a647d350..74e8bafb1 100644 --- a/app/views/crops/_predictions.html.haml +++ b/app/views/crops/_predictions.html.haml @@ -1,16 +1,17 @@ .predictions - unless crop.perennial.nil? .row - %p - #{crop.name} is - - if crop.perennial == true - = link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do - a perennial crop - (living more than two years) - - elsif crop.perennial == false - = link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do - an annual crop - (living and reproducing in a single year or less) + .col-md-12 + %p + #{crop.name} is + - if crop.perennial == true + = link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do + a perennial crop + (living more than two years) + - elsif crop.perennial == false + = link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do + an annual crop + (living and reproducing in a single year or less) .row - if crop.annual? && crop.median_lifespan.present? diff --git a/app/views/crops/show.html.haml b/app/views/crops/show.html.haml index e5e7e7a1a..8f4486fe9 100644 --- a/app/views/crops/show.html.haml +++ b/app/views/crops/show.html.haml @@ -23,25 +23,24 @@ .row .col-md-9 - - if member_signed_in? - = display_seed_availability(@current_member, @crop) - = link_to "View your seeds", seeds_by_owner_path(owner: current_member.slug) + .row + .col-md-12 + - if member_signed_in? + = display_seed_availability(@current_member, @crop) + = link_to "View your seeds", seeds_by_owner_path(owner: current_member.slug) + + %h2 + - if !@crop.plantings.empty? + = @crop.name.titleize + has been planted + = pluralize(@crop.plantings.size, "time") + by #{ENV['GROWSTUFF_SITE_NAME']} members. + - else + Nobody is growing this yet. You could be the first! - %h2 - - if !@crop.plantings.empty? - = @crop.name.titleize - has been planted - = pluralize(@crop.plantings.size, "time") - by #{ENV['GROWSTUFF_SITE_NAME']} members. - - else - Nobody is growing this yet. You could be the first! .row - .col-md-3 - %h2 Sunniness Chart - = pie_chart crop_sunniness_path(@crop), legend: "bottom" - - .col-md-9 + .col-md-12 %h2 Predictions = render 'predictions', crop: @crop @@ -49,7 +48,17 @@ .col-md-12 %h2 Photos %p= render 'crops/photos', crop: @crop + .row + .col-md-3 + %h2 Sunniness + = pie_chart crop_sunniness_path(@crop), legend: "bottom" + .col-md-3 + %h2 Planted from + = pie_chart crop_planted_from_path(@crop), legend: "bottom" + + .row + .col-md-12 %h2 Crop Map %p Only plantings by members who have set their locations are shown on this map. diff --git a/config/routes.rb b/config/routes.rb index 14c4bd401..2872db18b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -51,6 +51,7 @@ Growstuff::Application.routes.draw do resources :crops do get 'photos' => 'photos#index' get 'sunniness' => 'crops#sunniness' + get 'planted_from' => 'crops#planted_from' end resources :comments From 390282f9f231118db926810b1fe1ecc336c4e5d3 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 11 Jan 2018 09:03:13 +1300 Subject: [PATCH 083/219] Added Harvested for chart --- app/controllers/crops_controller.rb | 24 +++++++++++++++--------- app/views/crops/show.html.haml | 3 +++ config/routes.rb | 1 + 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/controllers/crops_controller.rb b/app/controllers/crops_controller.rb index 371e70726..d3bd5dcf9 100644 --- a/app/controllers/crops_controller.rb +++ b/app/controllers/crops_controller.rb @@ -103,23 +103,29 @@ class CropsController < ApplicationController end def sunniness - @crop = Crop.find(params[:crop_id]) - render json: Planting.where(crop: @crop) - .where.not(sunniness: nil) - .where.not(sunniness: '') - .group(:sunniness).count(:id) + pie_chart_query 'sunniness' end def planted_from + pie_chart_query 'planted_from' + end + + def harvested_for @crop = Crop.find(params[:crop_id]) - render json: Planting.where(crop: @crop) - .where.not(planted_from: nil) - .where.not(planted_from: '') - .group(:planted_from).count(:id) + render json: Harvest.joins(:plant_part).where(crop: @crop) + .group("plant_parts.name").count(:id) end private + def pie_chart_query(field) + @crop = Crop.find(params[:crop_id]) + render json: Planting.where(crop: @crop) + .where.not(field.to_sym => nil) + .where.not(field.to_sym => '') + .group(field.to_sym).count(:id) + end + def notifier case @crop.approval_status when "approved" diff --git a/app/views/crops/show.html.haml b/app/views/crops/show.html.haml index 8f4486fe9..270c0db2b 100644 --- a/app/views/crops/show.html.haml +++ b/app/views/crops/show.html.haml @@ -56,6 +56,9 @@ .col-md-3 %h2 Planted from = pie_chart crop_planted_from_path(@crop), legend: "bottom" + .col-md-3 + %h2 Harvested for + = pie_chart crop_harvested_for_path(@crop), legend: "bottom" .row .col-md-12 diff --git a/config/routes.rb b/config/routes.rb index 2872db18b..9d4e3fe60 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -52,6 +52,7 @@ Growstuff::Application.routes.draw do get 'photos' => 'photos#index' get 'sunniness' => 'crops#sunniness' get 'planted_from' => 'crops#planted_from' + get 'harvested_for' => 'crops#harvested_for' end resources :comments From bd326471781bf95582b32f5efa44b0a78e5494bd Mon Sep 17 00:00:00 2001 From: Awesome Code Date: Wed, 10 Jan 2018 20:17:58 +0000 Subject: [PATCH 084/219] Auto corrected by following Style/OrAssignment --- app/models/csv_importer.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/csv_importer.rb b/app/models/csv_importer.rb index 66297b13c..e24991e80 100644 --- a/app/models/csv_importer.rb +++ b/app/models/csv_importer.rb @@ -46,7 +46,7 @@ class CsvImporter names_to_add.each do |name| sciname = ScientificName.find_by(name: name, crop: @crop) - sciname = ScientificName.create!(name: name, crop: @crop, creator: cropbot) unless sciname + sciname ||= ScientificName.create!(name: name, crop: @crop, creator: cropbot) @crop.scientific_names << sciname end end @@ -56,13 +56,13 @@ class CsvImporter return if alternate_names.blank? alternate_names.split(/,\s*/).each do |name| altname = AlternateName.find_by(name: name, crop: @crop) - altname = AlternateName.create! name: name, crop: @crop, creator: cropbot unless altname + altname ||= AlternateName.create! name: name, crop: @crop, creator: cropbot @crop.alternate_names << altname end end def cropbot - @cropbot = Member.find_by!(login_name: 'cropbot') unless @cropbot + @cropbot ||= Member.find_by!(login_name: 'cropbot') @cropbot rescue raise "cropbot account not found: run rake db:seed" From e865e9366560aefffc4c6cdb43cc6550a4c5e3a7 Mon Sep 17 00:00:00 2001 From: Awesome Code Date: Wed, 10 Jan 2018 20:18:10 +0000 Subject: [PATCH 085/219] Auto corrected by following Style/ParallelAssignment --- app/mailers/notifier.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/mailers/notifier.rb b/app/mailers/notifier.rb index 569400ce6..6c479f51f 100644 --- a/app/mailers/notifier.rb +++ b/app/mailers/notifier.rb @@ -37,17 +37,20 @@ class Notifier < ActionMailer::Base end def new_crop_request(member, request) - @member, @request = member, request + @member = member + @request = request mail(to: @member.email, subject: "#{@request.requester.login_name} has requested #{@request.name} as a new crop") end def crop_request_approved(member, crop) - @member, @crop = member, crop + @member = member + @crop = crop mail(to: @member.email, subject: "#{crop.name.capitalize} has been approved") end def crop_request_rejected(member, crop) - @member, @crop = member, crop + @member = member + @crop = crop mail(to: @member.email, subject: "#{crop.name.capitalize} has been rejected") end end From 1a4e0bd48a473ac1b587215bda75cbe19a597b99 Mon Sep 17 00:00:00 2001 From: Awesome Code Date: Wed, 10 Jan 2018 20:18:17 +0000 Subject: [PATCH 086/219] Auto corrected by following Style/RescueStandardError --- app/models/csv_importer.rb | 2 +- db/seeds.rb | 2 +- spec/controllers/photo_associations_controller_spec.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/csv_importer.rb b/app/models/csv_importer.rb index 66297b13c..f95fae8f2 100644 --- a/app/models/csv_importer.rb +++ b/app/models/csv_importer.rb @@ -64,7 +64,7 @@ class CsvImporter def cropbot @cropbot = Member.find_by!(login_name: 'cropbot') unless @cropbot @cropbot - rescue + rescue StandardError raise "cropbot account not found: run rake db:seed" end end diff --git a/db/seeds.rb b/db/seeds.rb index 7df7e0e4c..be885a352 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -65,7 +65,7 @@ def load_test_users # rubocop:disable Metrics/AbcSize source_path = Rails.root.join('db', 'seeds') begin suburb_file = File.open("#{source_path}/suburbs.csv") - rescue + rescue StandardError puts "Warning: unable to open suburbs.csv" end diff --git a/spec/controllers/photo_associations_controller_spec.rb b/spec/controllers/photo_associations_controller_spec.rb index 75a04eedd..f021d5aa8 100644 --- a/spec/controllers/photo_associations_controller_spec.rb +++ b/spec/controllers/photo_associations_controller_spec.rb @@ -31,7 +31,7 @@ describe PhotoAssociationsController do expect do begin delete :destroy, valid_params - rescue + rescue StandardError nil end end.not_to change(photo.harvests, :count) From 0d35b4ca6d443b137d0119d46fa217ec50391c13 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 11 Jan 2018 09:26:48 +1300 Subject: [PATCH 087/219] Updating rubocop todo --- .rubocop.yml | 2 +- .rubocop_todo.yml | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 571a090bd..27309055f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -64,7 +64,7 @@ Metrics/MethodLength: Metrics/AbcSize: Max: 31 Metrics/ClassLength: - Max: 179 + Max: 182 Metrics/CyclomaticComplexity: Max: 11 Metrics/PerceivedComplexity: diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 4e0d899c7..1adfe8921 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,11 +1,16 @@ # This configuration was generated by # `rubocop --auto-gen-config --no-offense-counts` -# on 2017-12-06 11:20:15 +1300 using RuboCop version 0.49.1. +# on 2018-01-11 09:25:35 +1300 using RuboCop version 0.49.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. +# Cop supports --auto-correct. +Layout/EmptyLines: + Exclude: + - 'bin/setup' + Lint/HandleExceptions: Exclude: - 'lib/tasks/testing.rake' @@ -52,15 +57,10 @@ Style/AndOr: - 'config/unicorn.rb' - 'lib/tasks/growstuff.rake' -Style/AsciiComments: - Exclude: - - 'config/initializers/comfortable_mexican_sofa.rb' - # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: nested, compact Style/ClassAndModuleChildren: Exclude: - - 'app/controllers/admin/orders_controller.rb' - 'lib/actions/oauth_signup_action.rb' - 'lib/haml/filters/escaped_markdown.rb' @@ -111,3 +111,10 @@ Style/RegexpLiteral: - 'spec/views/devise/registrations/edit_spec.rb' - 'spec/views/members/index.html.haml_spec.rb' - 'spec/views/posts/index.html.haml_spec.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: single_quotes, double_quotes +Style/StringLiteralsInInterpolation: + Exclude: + - 'bin/yarn' From 7dab57f9bad636554bef011afe9b1cfd5759b7b9 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 11 Jan 2018 09:31:59 +1300 Subject: [PATCH 088/219] Removed d3 graph tests --- spec/javascripts/graphs/bar_group_spec.js | 54 -------------- .../graphs/bar_label_group_spec.js | 38 ---------- spec/javascripts/graphs/height_scale_spec.js | 35 --------- .../graphs/horizontal_bar_graph_spec.js | 74 ------------------- spec/javascripts/graphs/width_scale_spec.js | 40 ---------- 5 files changed, 241 deletions(-) delete mode 100644 spec/javascripts/graphs/bar_group_spec.js delete mode 100644 spec/javascripts/graphs/bar_label_group_spec.js delete mode 100644 spec/javascripts/graphs/height_scale_spec.js delete mode 100644 spec/javascripts/graphs/horizontal_bar_graph_spec.js delete mode 100644 spec/javascripts/graphs/width_scale_spec.js diff --git a/spec/javascripts/graphs/bar_group_spec.js b/spec/javascripts/graphs/bar_group_spec.js deleted file mode 100644 index a76992a6c..000000000 --- a/spec/javascripts/graphs/bar_group_spec.js +++ /dev/null @@ -1,54 +0,0 @@ -(function() { - 'use strict'; - - /* - These tests are for the BarGroup object. - */ - - describe('when drawing the group of bars', function() { - var BarGroup; var subject; var bars; var data; - - beforeEach(function() { - BarGroup = growstuff.BarGroup; - - bars = [ - {name: 'Shade', value: 0.2}, - {name: 'Half Shade', value: 0.5}, - ]; - - data = { - bars: bars, - bar_color: 'steelblue', - width: {size: 300, scale: 'linear'}, - height: {size: 400, scale: 'ordinal'}, - }; - - subject = new BarGroup(data); - subject.render(d3.select('#jasmine_content').append('svg')); - }); - - it('draws a group', function() { - expect($('g.bar')).toExist(); - }); - - it('draws 2 bars', function() { - expect($('g.bar rect')).toHaveLength(2); - }); - - it('fills the bars with color', function() { - expect($('g.bar rect')).toHaveAttr('fill', 'steelblue'); - }); - - it('shows a tooltip on hover', function() { - var i; - - // get all of the title nodes for the bars - var barNodes = $('g.bar rect title'); - - for (i = 0; i < bars.length; i++) { - expect(barNodes[i].textContent) - .toBe('This value is ' + bars[i].value + '' + '.'); - } - }); - }); -}()); diff --git a/spec/javascripts/graphs/bar_label_group_spec.js b/spec/javascripts/graphs/bar_label_group_spec.js deleted file mode 100644 index 7cb6e5537..000000000 --- a/spec/javascripts/graphs/bar_label_group_spec.js +++ /dev/null @@ -1,38 +0,0 @@ -(function() { - 'use strict'; - - /* - This file contains tests for the labels that get rendered next to each bar - */ - - describe('BarLabelGroup', function() { - var BarLabelGroup; var subject; var data; - - beforeEach(function() { - BarLabelGroup = growstuff.BarLabelGroup; - var bars = [ - {name: 'Shade', value: 0.2}, - {name: 'Half Shade', value: 0.5}, - ]; - data = { - bars: bars, - }; - subject = new BarLabelGroup(data); - subject.render(d3.select('#jasmine_content').append('svg')); - }); - - it('draws a group for labels', function() { - expect($('g.bar-label')).toExist(); - }); - - it('draws 2 bar labels', function() { - expect($('g.bar-label text')).toHaveLength(2); - }); - - it('has text for 2 bar labels', function() { - // jquery jasmine appends text from all text elements - // into one string - expect($('g.bar-label text')).toHaveText('ShadeHalf Shade'); - }); - }); -}()); diff --git a/spec/javascripts/graphs/height_scale_spec.js b/spec/javascripts/graphs/height_scale_spec.js deleted file mode 100644 index 3a257400c..000000000 --- a/spec/javascripts/graphs/height_scale_spec.js +++ /dev/null @@ -1,35 +0,0 @@ -(function() { - 'use strict'; - - /* - Tests for mapping the number of bars to the size of the svg - */ - - describe('HeightScale when specifying height', function() { - var data; var bars; var HeightScale; var subject; var mockD3; - - beforeEach(function() { - HeightScale = growstuff.HeightScale; - bars = [ - {name: 'Shade', value: 0.2}, - {name: 'Half Shade', value: 0.5}, - ]; - data = { - bars: bars, - width: {size: 300, scale: 'linear'}, - height: {size: 400, scale: 'ordinal'}, - }; - - subject = new HeightScale(data); - mockD3 = jasmine.createSpyObj('d3', ['domain', 'rangeRoundBands']); - mockD3.domain.and.returnValue(mockD3); - mockD3.rangeRoundBands.and.returnValue(mockD3); - spyOn(d3.scale, 'ordinal').and.returnValue(mockD3); - subject.render(); - }); - - it('calls the d3 range round bands function to draw the height', function() { - expect(mockD3.rangeRoundBands).toHaveBeenCalledWith([0, 400], 0.05, 0); - }); - }); -}()); diff --git a/spec/javascripts/graphs/horizontal_bar_graph_spec.js b/spec/javascripts/graphs/horizontal_bar_graph_spec.js deleted file mode 100644 index 16f35f018..000000000 --- a/spec/javascripts/graphs/horizontal_bar_graph_spec.js +++ /dev/null @@ -1,74 +0,0 @@ -(function() { - 'use strict'; - - /* - Tests in this file are for the pieces of HorizontalBarGraph or - are more integration-y type tests that require the full graph. - */ - - describe('HorizontalBarGraph', function() { - var BarLabelGroup; var BarGroup; var subject; var data; - - beforeEach(function() { - var HorizontalBarGraph = growstuff.HorizontalBarGraph; - var bars = [ - {name: 'Shade', value: 0.2}, - {name: 'Half Shade', value: 0.5}, - ]; - data = { - bars: bars, - bar_color: 'steelblue', - width: {size: 300, scale: 'linear'}, - height: {size: 400, scale: 'ordinal'}, - // left is used to shift the bars over so that there is - // room for the labels - margin: {top: 0, right: 0, bottom: 0, left: 100}, - }; - - subject = new HorizontalBarGraph(data); - BarGroup = growstuff.BarGroup; - BarLabelGroup = growstuff.BarLabelGroup; - expect(BarLabelGroup).toExist(); - spyOn(BarGroup.prototype, 'render').and.callThrough(); - spyOn(BarLabelGroup.prototype, 'render').and.callThrough(); - subject.render(d3.select($('#jasmine_content')[0])); - }); - - it('draws a graph', function() { - expect($('#jasmine_content svg')).toExist(); - }); - - it('draws a group for the whole graph', function() { - expect($('g.bar-graph')).toExist(); - }); - - it('draws a bar group', function() { - expect(BarGroup.prototype.render).toHaveBeenCalled(); - }); - - it('draws a group of bar labels', function() { - expect(BarLabelGroup.prototype.render).toHaveBeenCalled(); - }); - - it('has the expected width and height', function() { - var $svg = $('svg'); - var margin = data.margin; - expect($svg).toHaveAttr('width', (data.width.size + margin.left + margin.right) + ''); - expect($svg).toHaveAttr('height', (data.height.size + margin.top + margin.bottom) + ''); - }); - - it('draws the graph shifted to the right to accommodate for labels', function() { - expect('g.bar-graph').toHaveAttr('transform', 'translate(100,0)'); - }); - - it('on the x axis, draws at least one bar at max width less margin width', function() { - // because of domain and range mapping - expect('g.bar rect:eq(1)').toHaveAttr('width', '300' ); - }); - - it('on the y axis, all bars are the same height', function() { - expect('g.bar rect:eq(0)').toHaveAttr('height', '195'); - expect('g.bar rect:eq(1)').toHaveAttr('height', '195'); - }); - }); -}()); diff --git a/spec/javascripts/graphs/width_scale_spec.js b/spec/javascripts/graphs/width_scale_spec.js deleted file mode 100644 index d8e34234a..000000000 --- a/spec/javascripts/graphs/width_scale_spec.js +++ /dev/null @@ -1,40 +0,0 @@ -(function() { - 'use strict'; - - /* - This file contains tests for the mapping the data values to - the length of a bar so that it is the correct size for the screen - */ - - describe('GraphScale, when specifying width', function() { - var data; var WidthScale; var subject; var mockD3; - - beforeEach(function() { - WidthScale = growstuff.WidthScale; - var bars = [ - {name: 'Shade', value: 0.2}, - {name: 'Half Shade', value: 0.5}, - ]; - data = { - bars: bars, - width: {size: 300, scale: 'linear'}, - height: {size: 400, scale: 'ordinal'}, - }; - - subject = new WidthScale(data, 'width'); - mockD3 = jasmine.createSpyObj('d3', ['domain', 'range', 'max']); - mockD3.domain.and.returnValue(mockD3); - mockD3.range.and.returnValue(mockD3); - spyOn(d3.scale, 'linear').and.returnValue(mockD3); - subject.render(); - }); - - it('gets the value of the longest bar', function() { - expect(subject.getMaxValue()).toEqual(0.5); - }); - - it('calls the d3 range function to draw the width', function() { - expect(mockD3.range).toHaveBeenCalledWith([0, 300]); - }); - }); -}()); From 2d813e49de82071e10a6fa2b3102a3ea99da1897 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 11 Jan 2018 15:36:27 +1300 Subject: [PATCH 089/219] add google js to gardens#show --- app/views/gardens/show.html.haml | 1 - app/views/layouts/application.html.haml | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/gardens/show.html.haml b/app/views/gardens/show.html.haml index 5ffed7152..c6c08b792 100644 --- a/app/views/gardens/show.html.haml +++ b/app/views/gardens/show.html.haml @@ -11,7 +11,6 @@ = tag("meta", property: "og:site_name", content: ENV['GROWSTUFF_SITE_NAME']) - content_for :scripts do - = javascript_include_tag "charts" = javascript_include_tag "https://www.gstatic.com/charts/loader.js" .row diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index b90c025e7..7e9bee03d 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,6 +1,7 @@ !!! 5 %html{ lang: "en", prefix: "og: http://ogp.me/ns#" } = javascript_include_tag "application" + = yield :script = render partial: "layouts/meta" %body = render partial: "layouts/header" From c442bdbd45f6924d5af51c7ba340ee1ad0981fc2 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 11 Jan 2018 15:36:27 +1300 Subject: [PATCH 090/219] add google js to gardens#show --- app/views/gardens/show.html.haml | 1 - app/views/layouts/application.html.haml | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/gardens/show.html.haml b/app/views/gardens/show.html.haml index 5ffed7152..c6c08b792 100644 --- a/app/views/gardens/show.html.haml +++ b/app/views/gardens/show.html.haml @@ -11,7 +11,6 @@ = tag("meta", property: "og:site_name", content: ENV['GROWSTUFF_SITE_NAME']) - content_for :scripts do - = javascript_include_tag "charts" = javascript_include_tag "https://www.gstatic.com/charts/loader.js" .row diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index b90c025e7..7fa9b185f 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,5 +1,6 @@ !!! 5 %html{ lang: "en", prefix: "og: http://ogp.me/ns#" } + = yield :scripts = javascript_include_tag "application" = render partial: "layouts/meta" %body From cc1d3b4f43ec4ebebf193cacc3ee16ffb47ad21f Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Fri, 12 Jan 2018 08:07:38 +1300 Subject: [PATCH 091/219] Use predicted finish times to put current plantings on the timeline --- app/controllers/gardens_controller.rb | 11 ++++++++--- app/views/gardens/show.html.haml | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/controllers/gardens_controller.rb b/app/controllers/gardens_controller.rb index d2aa8bfe0..a8f664b6d 100644 --- a/app/controllers/gardens_controller.rb +++ b/app/controllers/gardens_controller.rb @@ -61,10 +61,15 @@ class GardensController < ApplicationController def timeline @data = [] @garden = Garden.find(params[:garden_id]) - @garden.plantings.where.not(finished_at: nil) - .where.not(planted_at: nil) + @garden.plantings.where.not(planted_at: nil) .order(finished_at: :desc).each do |p| - @data << [p.crop.name, p.planted_at, p.finished_at] + + finish = if p.finished_at.present? + p.finished_at + else + p.finish_predicted_at + end + @data << [p.crop.name, p.planted_at, finish] if finish.present? end render json: @data end diff --git a/app/views/gardens/show.html.haml b/app/views/gardens/show.html.haml index c6c08b792..3928cfa58 100644 --- a/app/views/gardens/show.html.haml +++ b/app/views/gardens/show.html.haml @@ -40,7 +40,7 @@ .row = timeline garden_timeline_path(@garden), adapter: "google" - %h3 What's planted here? + %h3 Current plantings in garden .row - if @current_plantings.size.positive? - @current_plantings.each do |planting| From b831c29c97d5117e8cbecb49e160a1771408ef8d Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Fri, 12 Jan 2018 08:07:38 +1300 Subject: [PATCH 092/219] Use .presence to find finished ts in timeline --- app/controllers/gardens_controller.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/controllers/gardens_controller.rb b/app/controllers/gardens_controller.rb index a8f664b6d..585966e54 100644 --- a/app/controllers/gardens_controller.rb +++ b/app/controllers/gardens_controller.rb @@ -63,12 +63,8 @@ class GardensController < ApplicationController @garden = Garden.find(params[:garden_id]) @garden.plantings.where.not(planted_at: nil) .order(finished_at: :desc).each do |p| - - finish = if p.finished_at.present? - p.finished_at - else - p.finish_predicted_at - end + # use finished_at if we have it, otherwise use predictions + finish = p.finished_at.presence || p.finish_predicted_at @data << [p.crop.name, p.planted_at, finish] if finish.present? end render json: @data From e8bcce2ad3a8762a486bde1ccfbd4c1944026b37 Mon Sep 17 00:00:00 2001 From: pozorvlak Date: Fri, 12 Jan 2018 11:31:28 +0000 Subject: [PATCH 093/219] Only deploy from the Elasticsearch build job Fixes #1099, or at least works around it, though a better solution would be to rewrite our deployment system using Travis build stages or Heroku build pipelines. The Elasticsearch job was picked because it's the one most likely to fail, and I can't work out how to deploy only if all three jobs pass. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ef68fec4f..5d94d6a86 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,6 +56,7 @@ deploy: secure: "WrQxf0fEKkCdXrjcejurobOnNNz3he4dDwjBbToXbQTQNDObPp7NetJrLsfM8FiUFEeOuvhIHHiDQtMvY720zGGAGxDptvgFS+0QHCUqoTRZA/yFfUmHlG2jROXTzk5uVK0AE4k6Ion5kX8+mM0EnMT/7u+MTFiukrJctSiEXfg=" on: repo: Growstuff/growstuff + condition: "$RSPEC_TAG = elasticsearch" app: dev: growstuff-staging master: growstuff-prod From 3a4443a4fe6497d91abeff6a5e390693ef0767d7 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 13 Jan 2018 22:11:02 +1300 Subject: [PATCH 094/219] Permissions (and specs) for charts json --- app/controllers/gardens_controller.rb | 2 +- app/models/ability.rb | 6 ++++++ spec/controllers/crops_controller_spec.rb | 16 ++++++++++++++++ spec/controllers/gardens_controller_spec.rb | 13 ++++++++++--- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/app/controllers/gardens_controller.rb b/app/controllers/gardens_controller.rb index 585966e54..a61d6cf88 100644 --- a/app/controllers/gardens_controller.rb +++ b/app/controllers/gardens_controller.rb @@ -1,5 +1,5 @@ class GardensController < ApplicationController - before_action :authenticate_member!, except: %i(index show) + before_action :authenticate_member!, except: %i(index show timeline) after_action :expire_homepage, only: %i(create delete) load_and_authorize_resource respond_to :html, :json diff --git a/app/models/ability.rb b/app/models/ability.rb index efba2aeb0..283bf45cc 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -15,6 +15,12 @@ class Ability can :view_follows, Member can :view_followers, Member + # Everyone can see the charts + can :timeline, Garden + can :sunniness, Crop + can :planted_from, Crop + can :harvested_for, Crop + # except these, which don't make sense if you're not logged in cannot :read, Notification cannot :read, Authentication diff --git a/spec/controllers/crops_controller_spec.rb b/spec/controllers/crops_controller_spec.rb index 5b682f6c2..9e54307bf 100644 --- a/spec/controllers/crops_controller_spec.rb +++ b/spec/controllers/crops_controller_spec.rb @@ -46,4 +46,20 @@ describe CropsController do it { expect(response.content_type).to eq("application/rss+xml") } end end + + describe 'GET charts' do + let(:crop) { FactoryBot.create :crop } + describe 'sunniness' do + before { get :sunniness, crop_id: crop.to_param } + it { expect(response).to be_success } + end + describe 'planted_from' do + before { get :planted_from, crop_id: crop.to_param } + it { expect(response).to be_success } + end + describe 'harvested_for' do + before { get :harvested_for, crop_id: crop.to_param } + it { expect(response).to be_success } + end + end end diff --git a/spec/controllers/gardens_controller_spec.rb b/spec/controllers/gardens_controller_spec.rb index 9253b5ddb..073d03d6b 100644 --- a/spec/controllers/gardens_controller_spec.rb +++ b/spec/controllers/gardens_controller_spec.rb @@ -4,9 +4,8 @@ RSpec.describe GardensController, type: :controller do include Devise::Test::ControllerHelpers let(:valid_params) { { name: 'My second Garden' } } + let(:garden) { FactoryBot.create :garden } context "when not signed in" do - let(:garden) { double('garden') } - describe 'GET new' do before { get :new, id: garden.to_param } it { expect(response).to redirect_to(new_member_session_path) } @@ -15,7 +14,10 @@ RSpec.describe GardensController, type: :controller do before { put :create, garden: valid_params } it { expect(response).to redirect_to(new_member_session_path) } end - + describe 'GET timeline' do + before { get :timeline, garden_id: garden.to_param } + it { expect(response).to be_success } + end describe 'changing existing records' do before do allow(Garden).to receive(:find).and_return(:garden) @@ -44,6 +46,11 @@ RSpec.describe GardensController, type: :controller do let!(:member) { FactoryBot.create(:member) } + describe 'GET timeline' do + before { get :timeline, garden_id: garden.to_param } + it { expect(response).to be_success } + end + describe "for another member's garden" do let(:not_my_garden) { double('garden') } From b6aef19be6543ce0b1179d697aa40913af44bfe9 Mon Sep 17 00:00:00 2001 From: Awesome Code Date: Sat, 13 Jan 2018 22:49:36 +0000 Subject: [PATCH 095/219] Auto corrected by following RSpec/MultipleSubjects --- spec/features/crops/crop_detail_page_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/crops/crop_detail_page_spec.rb b/spec/features/crops/crop_detail_page_spec.rb index 1ee9bae80..90ab42761 100644 --- a/spec/features/crops/crop_detail_page_spec.rb +++ b/spec/features/crops/crop_detail_page_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' feature "crop detail page", js: true do let(:crop) { create :crop } - subject { visit crop_path(crop) } + context "varieties" do scenario "The crop DOES NOT have varieties" do From 273e4c07d8198d113d9f7188f8a7ee91e4a668e1 Mon Sep 17 00:00:00 2001 From: Awesome Code Date: Sat, 13 Jan 2018 22:49:49 +0000 Subject: [PATCH 096/219] Auto corrected by following RSpec/NotToNot --- spec/features/crops/alternate_name_spec.rb | 2 +- spec/features/crops/crop_detail_page_spec.rb | 6 +++--- spec/features/following_spec.rb | 4 ++-- spec/features/member_profile_spec.rb | 6 +++--- spec/features/places/searching_a_place_spec.rb | 2 +- spec/features/plantings/planting_a_crop_spec.rb | 6 +++--- spec/features/scientific_name_spec.rb | 2 +- spec/features/shared_examples/crop_suggest.rb | 4 ++-- spec/models/crop_spec.rb | 2 +- spec/models/like_spec.rb | 10 +++++----- spec/models/post_spec.rb | 4 ++-- 11 files changed, 24 insertions(+), 24 deletions(-) diff --git a/spec/features/crops/alternate_name_spec.rb b/spec/features/crops/alternate_name_spec.rb index 319f4d7e1..e3b36c4a8 100644 --- a/spec/features/crops/alternate_name_spec.rb +++ b/spec/features/crops/alternate_name_spec.rb @@ -45,7 +45,7 @@ feature "Alternate names", js: true do href: alternate_name_path(alternate_eggplant) within('.alternate_names') { click_on "Delete" } expect(page.status_code).to equal 200 - expect(page).to_not have_content alternate_eggplant.name + expect(page).not_to have_content alternate_eggplant.name expect(page).to have_content 'Alternate name was successfully deleted' end diff --git a/spec/features/crops/crop_detail_page_spec.rb b/spec/features/crops/crop_detail_page_spec.rb index 1ee9bae80..b155d6df5 100644 --- a/spec/features/crops/crop_detail_page_spec.rb +++ b/spec/features/crops/crop_detail_page_spec.rb @@ -149,9 +149,9 @@ feature "crop detail page", js: true do scenario "User not signed in" do visit crop_path(seed.crop) - expect(page).to_not have_content "You have 20 seeds of this crop" - expect(page).to_not have_content "You don't have any seeds of this crop" - expect(page).to_not have_link "View your seeds" + expect(page).not_to have_content "You have 20 seeds of this crop" + expect(page).not_to have_content "You don't have any seeds of this crop" + expect(page).not_to have_link "View your seeds" end scenario "User signed in" do diff --git a/spec/features/following_spec.rb b/spec/features/following_spec.rb index ac7a207be..735883e77 100644 --- a/spec/features/following_spec.rb +++ b/spec/features/following_spec.rb @@ -6,8 +6,8 @@ feature "follows", :js do scenario "follow buttons on member profile page" do visit member_path(member) - expect(page).to_not have_link "Follow" - expect(page).to_not have_link "Unfollow" + expect(page).not_to have_link "Follow" + expect(page).not_to have_link "Unfollow" end end diff --git a/spec/features/member_profile_spec.rb b/spec/features/member_profile_spec.rb index 0c27c7be2..32b11bda1 100644 --- a/spec/features/member_profile_spec.rb +++ b/spec/features/member_profile_spec.rb @@ -125,8 +125,8 @@ feature "member profile", js: true do scenario "ordinary user's page" do visit member_path(other_member) - expect(page).to_not have_text "Crop Wrangler" - expect(page).to_not have_text "Admin" + expect(page).not_to have_text "Crop Wrangler" + expect(page).not_to have_text "Admin" end context "your own profile page" do @@ -163,7 +163,7 @@ feature "member profile", js: true do end scenario "does not have a button to edit profile" do - expect(page).to_not have_link "Edit profile", href: edit_member_registration_path + expect(page).not_to have_link "Edit profile", href: edit_member_registration_path end end end diff --git a/spec/features/places/searching_a_place_spec.rb b/spec/features/places/searching_a_place_spec.rb index 10797d917..edbb06e59 100644 --- a/spec/features/places/searching_a_place_spec.rb +++ b/spec/features/places/searching_a_place_spec.rb @@ -13,7 +13,7 @@ feature "User searches" do expect(page).to have_content "community near Philippines" expect(page).to have_button "search_button" expect(page).to have_content "Nearby members" - expect(page).to_not have_content "No results found" + expect(page).not_to have_content "No results found" end scenario "with a blank search string" do diff --git a/spec/features/plantings/planting_a_crop_spec.rb b/spec/features/plantings/planting_a_crop_spec.rb index 51ff69be2..0e26d7078 100644 --- a/spec/features/plantings/planting_a_crop_spec.rb +++ b/spec/features/plantings/planting_a_crop_spec.rb @@ -110,8 +110,8 @@ feature "Planting a crop", :js, :elasticsearch do end expect(page).to have_content "planting was successfully created" - expect(page).to_not have_content "Progress: 0% - not planted yet" - expect(page).to_not have_content "Not enough data" + expect(page).not_to have_content "Progress: 0% - not planted yet" + expect(page).not_to have_content "Not enough data" end it "should show that planting is 100% complete (no date specified)" do @@ -178,7 +178,7 @@ feature "Planting a crop", :js, :elasticsearch do fill_in "Finished date", with: "2015-06-25" click_button "Save" expect(page).to have_content "planting was successfully updated" - expect(page).to_not have_content "Progress: Not enough data" + expect(page).not_to have_content "Progress: Not enough data" end scenario "Marking a planting as finished" do diff --git a/spec/features/scientific_name_spec.rb b/spec/features/scientific_name_spec.rb index d7205d263..d6daf709b 100644 --- a/spec/features/scientific_name_spec.rb +++ b/spec/features/scientific_name_spec.rb @@ -45,7 +45,7 @@ feature "Scientific names", js: true do href: scientific_name_path(zea_mays) within('.scientific_names') { click_on "Delete" } expect(page.status_code).to equal 200 - expect(page).to_not have_content zea_mays.name + expect(page).not_to have_content zea_mays.name expect(page).to have_content 'Scientific name was successfully deleted.' end diff --git a/spec/features/shared_examples/crop_suggest.rb b/spec/features/shared_examples/crop_suggest.rb index 8f45c1e96..0d2e688a1 100644 --- a/spec/features/shared_examples/crop_suggest.rb +++ b/spec/features/shared_examples/crop_suggest.rb @@ -17,8 +17,8 @@ shared_examples "crop suggest" do |resource| fill_autocomplete "crop", with: "pe" end - expect(page).to_not have_content("pear") - expect(page).to_not have_content("pea") + expect(page).not_to have_content("pear") + expect(page).not_to have_content("pea") within "form#new_#{resource}" do fill_autocomplete "crop", with: "pea" diff --git a/spec/models/crop_spec.rb b/spec/models/crop_spec.rb index 515d0160c..9e6173221 100644 --- a/spec/models/crop_spec.rb +++ b/spec/models/crop_spec.rb @@ -553,7 +553,7 @@ describe Crop do end it "should not delete the posts" do - expect(Post.find(post.id)).to_not eq nil + expect(Post.find(post.id)).not_to eq nil end end end diff --git a/spec/models/like_spec.rb b/spec/models/like_spec.rb index 50a2c1116..c92e3c389 100644 --- a/spec/models/like_spec.rb +++ b/spec/models/like_spec.rb @@ -26,31 +26,31 @@ describe 'like' do it 'is invalid without a member' do like = Like.new like.likeable = post - expect(like).to_not be_valid + expect(like).not_to be_valid end it 'is invalid without a likeable item' do like = Like.new like.member = member - expect(like).to_not be_valid + expect(like).not_to be_valid end end it 'does not allow duplicate likes by the same member' do Like.create(member: member, likeable: post) second_like = Like.new(member: member, likeable: post) - expect(second_like).to_not be_valid + expect(second_like).not_to be_valid end it 'destroys like if post no longer exists' do like = Like.create(member: member, likeable: post) post.destroy - expect(Like.all).to_not include like + expect(Like.all).not_to include like end it 'destroys like if member no longer exists' do like = Like.create(member: member, likeable: post) member.destroy - expect(Like.all).to_not include like + expect(Like.all).not_to include like end end diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index 062aba487..98880ce2b 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -165,8 +165,8 @@ describe Post do end it "should not delete the crops" do - expect(Crop.find(tomato.id)).to_not eq nil - expect(Crop.find(maize.id)).to_not eq nil + expect(Crop.find(tomato.id)).not_to eq nil + expect(Crop.find(maize.id)).not_to eq nil end end end From 7fb2690306a449db2e11b4c21601f7595484e7b8 Mon Sep 17 00:00:00 2001 From: Awesome Code Date: Sat, 13 Jan 2018 22:51:04 +0000 Subject: [PATCH 097/219] Auto corrected by following Style/MutableConstant --- app/models/planting.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/planting.rb b/app/models/planting.rb index c51166264..6fd4aec17 100644 --- a/app/models/planting.rb +++ b/app/models/planting.rb @@ -4,12 +4,12 @@ class Planting < ActiveRecord::Base friendly_id :planting_slug, use: %i(slugged finders) # Constants - SUNNINESS_VALUES = %w(sun semi-shade shade) + SUNNINESS_VALUES = %w(sun semi-shade shade).freeze PLANTED_FROM_VALUES = [ 'seed', 'seedling', 'cutting', 'root division', 'runner', 'bulb', 'root/tuber', 'bare root plant', 'advanced plant', 'graft', 'layering' - ] + ].freeze ## ## Triggers From 4f0c004e50c9f9490bbd66095ba51145f07f1185 Mon Sep 17 00:00:00 2001 From: Shiny Date: Sun, 14 Jan 2018 12:04:32 +1300 Subject: [PATCH 098/219] Update crop_detail_page_spec.rb --- spec/features/crops/crop_detail_page_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/features/crops/crop_detail_page_spec.rb b/spec/features/crops/crop_detail_page_spec.rb index 90ab42761..03fb93ab8 100644 --- a/spec/features/crops/crop_detail_page_spec.rb +++ b/spec/features/crops/crop_detail_page_spec.rb @@ -3,8 +3,6 @@ require 'rails_helper' feature "crop detail page", js: true do let(:crop) { create :crop } - - context "varieties" do scenario "The crop DOES NOT have varieties" do visit crop_path(crop) From 8cdefb6fccce7804c22c50cb41c64a1ccccfad71 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 21 Jan 2018 18:36:23 +1300 Subject: [PATCH 099/219] Scope js variables so they are not global --- app/assets/javascripts/crops.js.erb | 26 +++++++++++++------------- app/assets/javascripts/members.js.erb | 19 +++++++++---------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/app/assets/javascripts/crops.js.erb b/app/assets/javascripts/crops.js.erb index 4e2a67e82..e4ee6d375 100644 --- a/app/assets/javascripts/crops.js.erb +++ b/app/assets/javascripts/crops.js.erb @@ -1,24 +1,27 @@ function showCropMap(cropmap) { + var mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>"; + var mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>"; + var mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token; L.tileLayer(mapbox_base_url, { attribution: 'Map data © OpenStreetMap contributors under ODbL | Map imagery © Mapbox', maxZoom: 18 }).addTo(cropmap); - markers = new L.MarkerClusterGroup({showCoverageOnHover: false, maxClusterRadius: 20 }); + var markers = new L.MarkerClusterGroup({showCoverageOnHover: false, maxClusterRadius: 20 }); - things_to_map = location.pathname + '.json'; + var things_to_map = location.pathname + '.json'; $.getJSON(things_to_map, function(crop) { $.each(crop.plantings, function(i, planting) { - owner = planting.owner; + var owner = planting.owner; if (owner.latitude && owner.longitude) { - marker = new L.Marker(new L.LatLng(owner.latitude, owner.longitude)); + var marker = new L.Marker(new L.LatLng(owner.latitude, owner.longitude)); - planting_url = "/plantings/" + planting.id; - planting_link = "" + owner.login_name + "'s " + crop.name + ""; + var planting_url = "/plantings/" + planting.id; + var planting_link = "" + owner.login_name + "'s " + crop.name + ""; - where = "

" + owner.location + "

"; + var where = "

" + owner.location + "

"; - details = "

"; + var details = "

"; if (planting.quantity) { details = details + "Quantity: " + planting.quantity + "
"; } @@ -41,13 +44,10 @@ function showCropMap(cropmap) { $(document).ready(function() { if (document.getElementById("cropmap") !== null) { - mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>"; - mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>"; - mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token; - L.Icon.Default.imagePath = '/assets' + L.Icon.Default.imagePath = '/assets'; - cropmap = L.map('cropmap').setView([0.0, -0.0], 2); + var cropmap = L.map('cropmap').setView([0.0, -0.0], 2); showCropMap(cropmap); } diff --git a/app/assets/javascripts/members.js.erb b/app/assets/javascripts/members.js.erb index 500e79b25..795387cc4 100644 --- a/app/assets/javascripts/members.js.erb +++ b/app/assets/javascripts/members.js.erb @@ -1,25 +1,24 @@ if (document.getElementById("membermap") !== null) { - mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>"; - mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>"; - mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token; - - L.Icon.Default.imagePath = '/assets' + var mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>"; + var mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>"; + var mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token; + L.Icon.Default.imagePath = '/assets'; $.getJSON(location.pathname + '.json', function(member) { if (member.latitude && member.longitude) { - membermap = L.map('membermap').setView([member.latitude, member.longitude], 4); + var membermap = L.map('membermap').setView([member.latitude, member.longitude], 4); L.tileLayer(mapbox_base_url, { attribution: 'Map data © OpenStreetMap contributors under ODbL | Map imagery © Mapbox', maxZoom: 18 }).addTo(membermap); - marker = new L.Marker(new L.LatLng(member.latitude, member.longitude)); + var marker = new L.Marker(new L.LatLng(member.latitude, member.longitude)); - member_url = "/members/" + member.slug; - member_link = "" + member.login_name + ""; + var member_url = "/members/" + member.slug; + var member_link = "" + member.login_name + ""; - where = "

" + member.location + "

"; + var where = "

" + member.location + "

"; marker.bindPopup(member_link + where).openPopup(); marker.addTo(membermap); From 700073e893d1923d7d75268a86c8f83603856399 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 11:02:22 +1200 Subject: [PATCH 100/219] Removed duplicate gardens/timeline code --- app/controllers/gardens_controller.rb | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/app/controllers/gardens_controller.rb b/app/controllers/gardens_controller.rb index 862a00ba0..69e80f893 100644 --- a/app/controllers/gardens_controller.rb +++ b/app/controllers/gardens_controller.rb @@ -60,18 +60,6 @@ class GardensController < ApplicationController redirect_to(gardens_by_owner_path(owner: @garden.owner)) end - def timeline - @data = [] - @garden = Garden.find(params[:garden_id]) - @garden.plantings.where.not(planted_at: nil) - .order(finished_at: :desc).each do |p| - # use finished_at if we have it, otherwise use predictions - finish = p.finished_at.presence || p.finish_predicted_at - @data << [p.crop.name, p.planted_at, finish] if finish.present? - end - render json: @data - end - private def garden_params From 2e339ce45d8096eefdf2c90fd2eaaac6aa929ab3 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 11:04:31 +1200 Subject: [PATCH 101/219] Adding rails version to new migrations --- db/migrate/20180118112809_add_datetaken_to_photos.rb | 2 +- db/migrate/20180205000612_remove_shop.rb | 2 +- db/migrate/20180213005731_seed_usage.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/migrate/20180118112809_add_datetaken_to_photos.rb b/db/migrate/20180118112809_add_datetaken_to_photos.rb index 42afaf23b..03fae1075 100644 --- a/db/migrate/20180118112809_add_datetaken_to_photos.rb +++ b/db/migrate/20180118112809_add_datetaken_to_photos.rb @@ -1,4 +1,4 @@ -class AddDatetakenToPhotos < ActiveRecord::Migration +class AddDatetakenToPhotos < ActiveRecord::Migration[4.2] def change add_column :photos, :date_taken, :datetime end diff --git a/db/migrate/20180205000612_remove_shop.rb b/db/migrate/20180205000612_remove_shop.rb index fa0fc8793..6977f127b 100644 --- a/db/migrate/20180205000612_remove_shop.rb +++ b/db/migrate/20180205000612_remove_shop.rb @@ -1,4 +1,4 @@ -class RemoveShop < ActiveRecord::Migration +class RemoveShop < ActiveRecord::Migration[4.2] def up drop_table :order_items drop_table :orders diff --git a/db/migrate/20180213005731_seed_usage.rb b/db/migrate/20180213005731_seed_usage.rb index f768451d2..0a94cb71a 100644 --- a/db/migrate/20180213005731_seed_usage.rb +++ b/db/migrate/20180213005731_seed_usage.rb @@ -1,4 +1,4 @@ -class SeedUsage < ActiveRecord::Migration +class SeedUsage < ActiveRecord::Migration[4.2] def change # # seed can be all sown, meaning there is none left add_column(:seeds, :finished, :boolean, default: false) From 7cda0aacc5786d79ca5eca786963cdd27da195ff Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 11:16:50 +1200 Subject: [PATCH 102/219] Passing class_name as string in members.requested_crops --- app/models/member.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/member.rb b/app/models/member.rb index f2427d45d..97f376551 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -20,7 +20,7 @@ class Member < ApplicationRecord has_many :sent_notifications, foreign_key: 'sender_id' has_many :authentications has_many :photos - has_many :requested_crops, class_name: Crop, foreign_key: 'requester_id' + has_many :requested_crops, class_name: 'Crop', foreign_key: 'requester_id' has_many :likes, dependent: :destroy has_many :follows, class_name: "Follow", foreign_key: "follower_id", dependent: :destroy has_many :inverse_follows, class_name: "Follow", foreign_key: "followed_id", dependent: :destroy From 4d21e132b988585a6072e1116df6d8c87d8ea137 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 11:18:56 +1200 Subject: [PATCH 103/219] Timeline is in a different controller now --- app/controllers/gardens_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/gardens_controller.rb b/app/controllers/gardens_controller.rb index 69e80f893..35efaf5d2 100644 --- a/app/controllers/gardens_controller.rb +++ b/app/controllers/gardens_controller.rb @@ -1,5 +1,5 @@ class GardensController < ApplicationController - before_action :authenticate_member!, except: %i(index show timeline) + before_action :authenticate_member!, except: %i(index show) after_action :expire_homepage, only: %i(create delete) load_and_authorize_resource respond_to :html, :json From 49d3f24ac8b01cd54b8c1de282c25034c186f09a Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 11:19:40 +1200 Subject: [PATCH 104/219] [Corrected] Use the -> { ... } lambda literal syntax for single line lambdas. --- spec/controllers/member_controller_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/controllers/member_controller_spec.rb b/spec/controllers/member_controller_spec.rb index dada0b013..f4bab7ff8 100644 --- a/spec/controllers/member_controller_spec.rb +++ b/spec/controllers/member_controller_spec.rb @@ -44,12 +44,12 @@ describe MembersController do end it "doesn't show completely nonsense members" do - lambda { get :show, params: { id: 9999 } }.should raise_error(ActiveRecord::RecordNotFound) + -> { get :show, params: { id: 9999 } }.should raise_error(ActiveRecord::RecordNotFound) end it "doesn't show unconfirmed members" do @member2 = FactoryBot.create(:unconfirmed_member) - lambda { get :show, params: { id: @member2.id } }.should raise_error(ActiveRecord::RecordNotFound) + -> { get :show, params: { id: @member2.id } }.should raise_error(ActiveRecord::RecordNotFound) end end From 41ea871c800071d2b66dc9131bb4e1283233f87c Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 11:20:28 +1200 Subject: [PATCH 105/219] Comment out comfy admin for now --- config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index 7ef095562..046c1b261 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -83,7 +83,7 @@ Rails.application.routes.draw do get 'auth/:provider/callback' => 'authentications#create' get 'members/auth/:provider/callback' => 'authentications#create' - comfy_route :cms_admin, path: '/admin/cms' + # comfy_route :cms_admin, path: '/admin/cms' namespace :admin do resources :members end From f061a1d9c7bc19f1e17000e7a38f74f8bb4444ac Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 11:33:43 +1200 Subject: [PATCH 106/219] Allow planting to have null parent_seed --- app/models/planting.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/planting.rb b/app/models/planting.rb index f985565a7..75dd64fa2 100644 --- a/app/models/planting.rb +++ b/app/models/planting.rb @@ -23,7 +23,8 @@ class Planting < ApplicationRecord # # Ancestry of food - belongs_to :parent_seed, class_name: 'Seed', foreign_key: 'parent_seed_id' # parent + belongs_to :parent_seed, class_name: 'Seed', foreign_key: 'parent_seed_id', + allow_nil: true # parent has_many :child_seeds, class_name: 'Seed', foreign_key: 'parent_planting_id', dependent: :nullify # children From 84e47b721bf69db68ba8471dc891ce0c1f5affe6 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 11:36:16 +1200 Subject: [PATCH 107/219] removed duplicate crop chart methods --- app/controllers/crops_controller.rb | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/app/controllers/crops_controller.rb b/app/controllers/crops_controller.rb index 67832c9b1..361e28031 100644 --- a/app/controllers/crops_controller.rb +++ b/app/controllers/crops_controller.rb @@ -106,30 +106,8 @@ class CropsController < ApplicationController respond_with @crop end - def sunniness - pie_chart_query 'sunniness' - end - - def planted_from - pie_chart_query 'planted_from' - end - - def harvested_for - @crop = Crop.find(params[:crop_id]) - render json: Harvest.joins(:plant_part).where(crop: @crop) - .group("plant_parts.name").count(:id) - end - private - def pie_chart_query(field) - @crop = Crop.find(params[:crop_id]) - render json: Planting.where(crop: @crop) - .where.not(field.to_sym => nil) - .where.not(field.to_sym => '') - .group(field.to_sym).count(:id) - end - def notifier case @crop.approval_status when "approved" From e43740bd3ddc5465969aa95f10369aa50b97297a Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 15:05:46 +1200 Subject: [PATCH 108/219] Two more application name renames --- app/assets/javascripts/crops.js.erb | 4 ++-- app/views/layouts/application.html.haml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/crops.js.erb b/app/assets/javascripts/crops.js.erb index e4ee6d375..cac1804f1 100644 --- a/app/assets/javascripts/crops.js.erb +++ b/app/assets/javascripts/crops.js.erb @@ -1,6 +1,6 @@ function showCropMap(cropmap) { - var mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>"; - var mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>"; + var mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Rails.application.config.mapbox_map_id %>"; + var mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Rails.application.config.mapbox_access_token %>"; var mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token; L.tileLayer(mapbox_base_url, { diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 6eea27068..be43bb0c5 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -28,4 +28,4 @@ \================================================== / Placed at the end of the document so the pages load faster = javascript_include_tag "application" - != Growstuff::Application.config.analytics_code + != Rails.application.config.analytics_code From 6149d203902743e6ded40a0900861c9ea009827a Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 15:09:02 +1200 Subject: [PATCH 109/219] Ignore highcharts.js --- .overcommit.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.overcommit.yml b/.overcommit.yml index d7899885f..a38401c1e 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -63,6 +63,7 @@ PreCommit: - 'app/assets/**' - 'spec/javascripts/support/vendor/**' - '**/bootstrap*' + - 'app/assets/javascripts/highcharts.js' command: ['./node_modules/.bin/eslint'] required_executable: 'npm' ScssLint: From 5683f1939d6a0a5c0ee33b160d25daac1a218595 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 15:17:28 +1200 Subject: [PATCH 110/219] Fixed up planting model, parent is not required --- app/models/planting.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/planting.rb b/app/models/planting.rb index 75dd64fa2..7b3c90901 100644 --- a/app/models/planting.rb +++ b/app/models/planting.rb @@ -24,7 +24,7 @@ class Planting < ApplicationRecord # # Ancestry of food belongs_to :parent_seed, class_name: 'Seed', foreign_key: 'parent_seed_id', - allow_nil: true # parent + required: false # parent has_many :child_seeds, class_name: 'Seed', foreign_key: 'parent_planting_id', dependent: :nullify # children From d19c9ab66996b890594844c4d57c4cf02e6964b3 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 15:30:21 +1200 Subject: [PATCH 111/219] Remove duplicate scripts yield in layout --- app/views/layouts/application.html.haml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index be43bb0c5..f45392712 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,8 +1,6 @@ !!! 5 %html{ lang: "en", prefix: "og: http://ogp.me/ns#" } = yield :scripts - = javascript_include_tag "application" - = yield :script = render partial: "layouts/meta" %body = render partial: "layouts/header" From 91ee0150f1bef12deaeafe8a389419a1409f6abf Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 10:29:10 +1200 Subject: [PATCH 112/219] Adding dependent clauses to models --- app/models/crop.rb | 6 +++--- app/models/member.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/crop.rb b/app/models/crop.rb index 9f1ec6e59..3e8b3981b 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -11,10 +11,10 @@ class Crop < ApplicationRecord has_many :scientific_names, after_add: :update_index, after_remove: :update_index, dependent: :destroy accepts_nested_attributes_for :scientific_names, allow_destroy: true, reject_if: :all_blank has_many :alternate_names, after_add: :update_index, after_remove: :update_index, dependent: :destroy - has_many :plantings + has_many :plantings, dependent: :destroy + has_many :seeds, dependent: :destroy + has_many :harvests, dependent: :destroy has_many :photos, through: :plantings - has_many :seeds - has_many :harvests has_many :plant_parts, -> { distinct.reorder("plant_parts.name") }, through: :harvests belongs_to :creator, class_name: 'Member', optional: true belongs_to :requester, class_name: 'Member', optional: true diff --git a/app/models/member.rb b/app/models/member.rb index 97f376551..72bce256f 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -20,7 +20,7 @@ class Member < ApplicationRecord has_many :sent_notifications, foreign_key: 'sender_id' has_many :authentications has_many :photos - has_many :requested_crops, class_name: 'Crop', foreign_key: 'requester_id' + has_many :requested_crops, class_name: 'Crop', foreign_key: 'requester_id', dependent: :nullify has_many :likes, dependent: :destroy has_many :follows, class_name: "Follow", foreign_key: "follower_id", dependent: :destroy has_many :inverse_follows, class_name: "Follow", foreign_key: "followed_id", dependent: :destroy From 11fd95e2b859371da6452976fc30f2189c8c46bb Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 15:45:06 +1200 Subject: [PATCH 113/219] Upgrading rubocop and haml_lint to match hound --- .rubocop.yml | 2 +- Gemfile | 5 ++--- Gemfile.lock | 17 ++++++++--------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 27309055f..9f6c64521 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -12,7 +12,7 @@ AllCops: Rails: Enabled: true -Style/FileName: +Naming/FileName: Exclude: - 'Guardfile' - 'Gemfile' diff --git a/Gemfile b/Gemfile index a6cb43608..42b698280 100644 --- a/Gemfile +++ b/Gemfile @@ -133,13 +133,12 @@ group :development, :test do gem 'faker' gem 'haml-i18n-extractor' gem 'haml-rails' # HTML templating language - gem 'haml_lint' # Checks haml files for goodness + gem 'haml_lint', '>= 0.25.1' # Checks haml files for goodness gem 'i18n-tasks' # adds tests for finding missing and unused translations gem 'poltergeist' # for headless JS testing - gem 'rainbow', '< 2.2.0' # See https://github.com/sickill/rainbow/issues/44 gem 'rspec-activemodel-mocks' gem 'rspec-rails' # unit testing framework - gem 'rubocop' + gem 'rubocop', '>= 0.54.0' gem 'selenium-webdriver' gem 'webrat' # provides HTML matchers for view tests end diff --git a/Gemfile.lock b/Gemfile.lock index cc5af9002..be1b6485e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -180,11 +180,11 @@ GEM haml (>= 4.0.6, < 6.0) html2haml (>= 1.0.1) railties (>= 4.0.1) - haml_lint (0.26.0) + haml_lint (0.27.0) haml (>= 4.0, < 5.1) rainbow rake (>= 10, < 13) - rubocop (>= 0.49.0) + rubocop (>= 0.50.0) sysexits (~> 1.1) hashie (3.5.7) heroics (0.0.24) @@ -348,7 +348,7 @@ GEM method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rainbow (2.1.0) + rainbow (3.0.0) raindrops (0.19.0) rake (12.3.1) rb-fsevent (0.10.3) @@ -379,11 +379,11 @@ GEM rspec-mocks (~> 3.7.0) rspec-support (~> 3.7.0) rspec-support (3.7.1) - rubocop (0.49.1) + rubocop (0.54.0) parallel (~> 1.10) - parser (>= 2.3.3.1, < 3.0) + parser (>= 2.5) powerpack (~> 0.1) - rainbow (>= 1.99.1, < 3.0) + rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-progressbar (1.9.0) @@ -504,7 +504,7 @@ DEPENDENCIES haml haml-i18n-extractor haml-rails - haml_lint + haml_lint (>= 0.25.1) hashie (>= 3.5.3) i18n-tasks jquery-rails @@ -531,12 +531,11 @@ DEPENDENCIES rails-assets-leaflet.markercluster! rails-controller-testing rails_12factor - rainbow (< 2.2.0) rake (>= 10.0.0) responders rspec-activemodel-mocks rspec-rails - rubocop + rubocop (>= 0.54.0) ruby-units sass-rails selenium-webdriver From a00237dfcba285aeaaf930684ca17f2001b973e0 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 15:48:16 +1200 Subject: [PATCH 114/219] Style fixes after rubocop upgrade --- Rakefile | 2 +- app/controllers/likes_controller.rb | 4 ++-- app/controllers/robots_controller.rb | 2 +- app/helpers/application_helper.rb | 4 +--- app/helpers/plantings_helper.rb | 4 ++-- app/models/csv_importer.rb | 4 ++-- app/models/garden.rb | 2 +- app/models/harvest.rb | 4 ++-- app/models/member.rb | 4 +--- app/models/photo.rb | 2 +- app/models/planting.rb | 4 +--- bin/bundle | 2 +- bin/setup | 2 +- bin/update | 2 +- bin/yarn | 4 ++-- config/setup_load_paths.rb | 2 +- script/rails | 4 ++-- spec/models/member_spec.rb | 8 ++++---- spec/models/post_spec.rb | 2 +- spec/rails_helper.rb | 2 +- 20 files changed, 29 insertions(+), 35 deletions(-) diff --git a/Rakefile b/Rakefile index 22553c357..15ae3eb68 100755 --- a/Rakefile +++ b/Rakefile @@ -3,6 +3,6 @@ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. require 'rake/dsl_definition' -require File.expand_path('../config/application', __FILE__) +require File.expand_path('config/application', __dir__) Rails.application.load_tasks diff --git a/app/controllers/likes_controller.rb b/app/controllers/likes_controller.rb index 5f7c5dac9..0fdc2f407 100644 --- a/app/controllers/likes_controller.rb +++ b/app/controllers/likes_controller.rb @@ -11,7 +11,7 @@ class LikesController < ApplicationController def destroy @like = Like.find_by(id: params[:id], member: current_member) - return failed(@like, message: 'Unable to unlike') unless @like && @like.destroy + return failed(@like, message: 'Unable to unlike') unless @like&.destroy success(@like, liked_by_member: false, status_code: :ok) end @@ -46,7 +46,7 @@ class LikesController < ApplicationController format.json { render(json: { 'error': message }, status: :forbidden) } format.html do flash[:error] = message - if like && like.likeable + if like&.likeable redirect_to like.likeable else redirect_to root_path diff --git a/app/controllers/robots_controller.rb b/app/controllers/robots_controller.rb index eb846d23d..84f77e906 100644 --- a/app/controllers/robots_controller.rb +++ b/app/controllers/robots_controller.rb @@ -10,6 +10,6 @@ class RobotsController < ApplicationController private def subdomain - request.subdomain.present? ? request.subdomain : nil + request.subdomain.presence end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 4e010cef1..ad63f37e2 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -61,9 +61,7 @@ module ApplicationHelper # http://graph.facebook.com/12345678/picture?width=150&height=150 uri = URI.parse(member.preferred_avatar_uri) - if uri.host == 'graph.facebook.com' - uri.query = "&width=#{size}&height=#{size}" - end + uri.query = "&width=#{size}&height=#{size}" if uri.host == 'graph.facebook.com' # TODO: Assess twitter - https://dev.twitter.com/overview/general/user-profile-images-and-banners # TODO: Assess flickr - https://www.flickr.com/services/api/misc.buddyicons.html diff --git a/app/helpers/plantings_helper.rb b/app/helpers/plantings_helper.rb index f71dc8158..26d0ec6c4 100644 --- a/app/helpers/plantings_helper.rb +++ b/app/helpers/plantings_helper.rb @@ -10,11 +10,11 @@ module PlantingsHelper end def display_planted_from(planting) - planting.planted_from.present? ? planting.planted_from : "not specified" + planting.planted_from.presence || "not specified" end def display_planting_quantity(planting) - planting.quantity.present? ? planting.quantity : "not specified" + planting.quantity.presence || "not specified" end def display_planting(planting) diff --git a/app/models/csv_importer.rb b/app/models/csv_importer.rb index 2f646b6ab..4205a6c13 100644 --- a/app/models/csv_importer.rb +++ b/app/models/csv_importer.rb @@ -9,7 +9,7 @@ class CsvImporter name, en_wikipedia_url, parent_name, scientific_names, alternate_names = row @crop = Crop.find_or_create_by(name: name) - @crop.update_attributes( + @crop.update( en_wikipedia_url: en_wikipedia_url, creator_id: cropbot.id ) @@ -26,7 +26,7 @@ class CsvImporter def add_parent(parent_name) parent = Crop.find_by(name: parent_name) if parent - @crop.update_attributes(parent_id: parent.id) + @crop.update(parent_id: parent.id) else @crop.logger.warn("Warning: parent crop #{parent_name} not found") end diff --git a/app/models/garden.rb b/app/models/garden.rb index 096647035..2946af1cf 100644 --- a/app/models/garden.rb +++ b/app/models/garden.rb @@ -48,7 +48,7 @@ class Garden < ApplicationRecord after_validation :cleanup_area def cleanup_area - self.area = nil if area && area.zero? + self.area = nil if area&.zero? self.area_unit = nil if area.blank? end diff --git a/app/models/harvest.rb b/app/models/harvest.rb index 60a43ce44..3a96afdbb 100644 --- a/app/models/harvest.rb +++ b/app/models/harvest.rb @@ -82,9 +82,9 @@ class Harvest < ApplicationRecord end def cleanup_quantities - self.quantity = nil if quantity && quantity.zero? + self.quantity = nil if quantity&.zero? self.unit = nil if quantity.blank? - self.weight_quantity = nil if weight_quantity && weight_quantity.zero? + self.weight_quantity = nil if weight_quantity&.zero? self.weight_unit = nil if weight_quantity.blank? end diff --git a/app/models/member.rb b/app/models/member.rb index 72bce256f..d0ba369cc 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -157,9 +157,7 @@ class Member < ApplicationRecord nearby_members = [] if place latitude, longitude = Geocoder.coordinates(place, params: { limit: 1 }) - if latitude && longitude - nearby_members = Member.located.sort_by { |x| x.distance_from([latitude, longitude]) } - end + nearby_members = Member.located.sort_by { |x| x.distance_from([latitude, longitude]) } if latitude && longitude end nearby_members end diff --git a/app/models/photo.rb b/app/models/photo.rb index c9c8c6a94..67aac82f3 100644 --- a/app/models/photo.rb +++ b/app/models/photo.rb @@ -51,7 +51,7 @@ class Photo < ApplicationRecord end def set_flickr_metadata! - update_attributes(flickr_metadata) + update(flickr_metadata) end def to_s diff --git a/app/models/planting.rb b/app/models/planting.rb index 7b3c90901..a4521f6ca 100644 --- a/app/models/planting.rb +++ b/app/models/planting.rb @@ -95,9 +95,7 @@ class Planting < ApplicationRecord end def expected_lifespan - if planted_at.present? && finished_at.present? - return (finished_at - planted_at).to_i - end + return (finished_at - planted_at).to_i if planted_at.present? && finished_at.present? crop.median_lifespan end diff --git a/bin/bundle b/bin/bundle index 66e9889e8..f19acf5b5 100755 --- a/bin/bundle +++ b/bin/bundle @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) load Gem.bin_path('bundler', 'bundle') diff --git a/bin/setup b/bin/setup index 78c4e861d..d5163f8c1 100755 --- a/bin/setup +++ b/bin/setup @@ -4,7 +4,7 @@ require 'fileutils' include FileUtils # path to your application root. -APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) +APP_ROOT = Pathname.new File.expand_path('..', __dir__) def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") diff --git a/bin/update b/bin/update index a8e4462f2..32326c74f 100755 --- a/bin/update +++ b/bin/update @@ -4,7 +4,7 @@ require 'fileutils' include FileUtils # path to your application root. -APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) +APP_ROOT = Pathname.new File.expand_path('..', __dir__) def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") diff --git a/bin/yarn b/bin/yarn index c2bacef83..ad111a882 100755 --- a/bin/yarn +++ b/bin/yarn @@ -4,8 +4,8 @@ Dir.chdir(VENDOR_PATH) do begin exec "yarnpkg #{ARGV.join(" ")}" rescue Errno::ENOENT - $stderr.puts "Yarn executable was not detected in the system." - $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" + warn "Yarn executable was not detected in the system." + warn "Download Yarn at https://yarnpkg.com/en/docs/install" exit 1 end end diff --git a/config/setup_load_paths.rb b/config/setup_load_paths.rb index c1a0f95fd..b78f9aff0 100644 --- a/config/setup_load_paths.rb +++ b/config/setup_load_paths.rb @@ -1,4 +1,4 @@ -if ENV['MY_RUBY_HOME'] && ENV['MY_RUBY_HOME'].include?('rvm') +if ENV['MY_RUBY_HOME']&.include?('rvm') begin require 'rvm' RVM.use_from_path! File.dirname(File.dirname(__FILE__)) diff --git a/script/rails b/script/rails index 9092f9f3c..754b05caa 100755 --- a/script/rails +++ b/script/rails @@ -2,6 +2,6 @@ # This command will automatically be run when you run "rails" # with Rails 3 gems installed from the root of your application. -APP_PATH = File.expand_path('../../config/application', __FILE__) -require File.expand_path('../../config/boot', __FILE__) +APP_PATH = File.expand_path('../config/application', __dir__) +require File.expand_path('../config/boot', __dir__) require 'rails/commands' diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index e02935d8e..f107ceb7f 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -67,22 +67,22 @@ describe 'member' do end it 'has location and lat/long fields' do - member.update_attributes(location: 'Greenwich, UK') + member.update(location: 'Greenwich, UK') member.location.should eq 'Greenwich, UK' member.latitude.round(2).should eq 51.48 member.longitude.round(2).should eq 0.00 end it 'empties the lat/long if location removed' do - member.update_attributes(location: 'Greenwich, UK') - member.update_attributes(location: '') + member.update(location: 'Greenwich, UK') + member.update(location: '') member.location.should eq '' member.latitude.should be_nil member.longitude.should be_nil end it 'fails gracefully for unfound locations' do - member.update_attributes(location: 'Tatooine') + member.update(location: 'Tatooine') member.location.should eq 'Tatooine' member.latitude.should be_nil member.longitude.should be_nil diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index c4fc3d020..7ae50f7f2 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -145,7 +145,7 @@ describe Post do end it "should be updated when post was modified" do - post.update_attributes(body: "[chard](crop)") + post.update(body: "[chard](crop)") expect(post.crops).to eq [chard] expect(chard.posts).to eq [post] diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index e28dd6e24..862b0316d 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -17,7 +17,7 @@ SimpleCov.start :rails do end require 'spec_helper' -require File.expand_path("../../config/environment", __FILE__) +require File.expand_path('../config/environment', __dir__) require 'rspec/rails' # Add additional requires below this line. Rails is not loaded until this point! Rails.application.eager_load! From 0235690f826748463016166c747fc4f823913c84 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 15:58:36 +1200 Subject: [PATCH 115/219] More dependent declared on relationships --- app/models/member.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/models/member.rb b/app/models/member.rb index d0ba369cc..e42d2f11c 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -8,17 +8,17 @@ class Member < ApplicationRecord # # Relationships - has_many :posts, foreign_key: 'author_id' - has_many :comments, foreign_key: 'author_id' - has_many :forums, foreign_key: 'owner_id' - has_many :gardens, foreign_key: 'owner_id' - has_many :plantings, foreign_key: 'owner_id' - has_many :seeds, foreign_key: 'owner_id' - has_many :harvests, foreign_key: 'owner_id' + has_many :posts, foreign_key: 'author_id', dependent: :destroy + has_many :comments, foreign_key: 'author_id', dependent: :destroy + has_many :forums, foreign_key: 'owner_id', dependent: :nullify + has_many :gardens, foreign_key: 'owner_id', dependent: :destroy + has_many :plantings, foreign_key: 'owner_id', dependent: :destroy + has_many :seeds, foreign_key: 'owner_id', dependent: :destroy + has_many :harvests, foreign_key: 'owner_id', dependent: :destroy has_and_belongs_to_many :roles # rubocop:disable Rails/HasAndBelongsToMany has_many :notifications, foreign_key: 'recipient_id' has_many :sent_notifications, foreign_key: 'sender_id' - has_many :authentications + has_many :authentications, dependent: :destroy has_many :photos has_many :requested_crops, class_name: 'Crop', foreign_key: 'requester_id', dependent: :nullify has_many :likes, dependent: :destroy From 5dbb3a1487b79592d277918b05c0cb4d46dcfa89 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 15:59:38 +1200 Subject: [PATCH 116/219] Mark cms specs as pending --- spec/features/cms_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/features/cms_spec.rb b/spec/features/cms_spec.rb index 176c531a8..fa6ace64a 100644 --- a/spec/features/cms_spec.rb +++ b/spec/features/cms_spec.rb @@ -4,13 +4,13 @@ feature "cms admin" do let(:member) { create :member } let(:admin_member) { create :admin_member } - scenario "can't view CMS admin if not signed in" do + pending "can't view CMS admin if not signed in" do visit comfy_admin_cms_path expect(current_path).to eq root_path expect(page).to have_content "Please sign in as an admin user" end - scenario "can't view CMS admin if not an admin member" do + pending "can't view CMS admin if not an admin member" do # sign in as an ordinary member login_as member visit comfy_admin_cms_path @@ -18,7 +18,7 @@ feature "cms admin" do expect(page).to have_content "Please sign in as an admin user" end - scenario "admin members can view CMS admin area" do + pending "admin members can view CMS admin area" do login_as admin_member visit comfy_admin_cms_path expect(current_path).to match(/#{comfy_admin_cms_path}/) # match any CMS admin page From c967f7432e545e258c6011ce5d320a436f2fae8b Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 16:04:52 +1200 Subject: [PATCH 117/219] Seed's parent planting is optional --- app/models/seed.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/seed.rb b/app/models/seed.rb index dff707a5c..ff687a4eb 100644 --- a/app/models/seed.rb +++ b/app/models/seed.rb @@ -14,7 +14,8 @@ class Seed < ApplicationRecord belongs_to :crop belongs_to :owner, class_name: 'Member', foreign_key: 'owner_id', counter_cache: true - belongs_to :parent_planting, class_name: 'Planting', foreign_key: 'parent_planting_id' # parent + belongs_to :parent_planting, class_name: 'Planting', foreign_key: 'parent_planting_id', + required: false # parent has_many :child_plantings, class_name: 'Planting', foreign_key: 'parent_seed_id', dependent: :nullify # children From 7f8bdfe48f4d6c4d13c97a94707b409f8930ded8 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 16:09:41 +1200 Subject: [PATCH 118/219] Updated rubocop todo --- .rubocop.yml | 8 ++--- .rubocop_todo.yml | 83 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 80 insertions(+), 11 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 9f6c64521..9a8a0a405 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -62,10 +62,10 @@ Metrics/BlockLength: Metrics/MethodLength: Max: 34 Metrics/AbcSize: - Max: 31 + Max: 30 Metrics/ClassLength: - Max: 182 + Max: 172 Metrics/CyclomaticComplexity: - Max: 11 + Max: 7 Metrics/PerceivedComplexity: - Max: 9 + Max: 8 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 29056f7be..99782950a 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --no-offense-counts` -# on 2018-02-05 14:37:22 +1300 using RuboCop version 0.49.1. +# on 2018-04-02 16:07:34 +1200 using RuboCop version 0.54.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -15,6 +15,13 @@ Lint/HandleExceptions: Exclude: - 'lib/tasks/testing.rake' +# Configuration parameters: MaximumRangeSize. +Lint/MissingCopEnableDirective: + Exclude: + - 'config.rb' + - 'config/compass.rb' + - 'config/initializers/devise.rb' + # Cop supports --auto-correct. # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods. Lint/UnusedMethodArgument: @@ -24,10 +31,51 @@ Lint/UnusedMethodArgument: - 'app/controllers/registrations_controller.rb' - 'app/validators/approved_validator.rb' +Lint/UriEscapeUnescape: + Exclude: + - 'app/helpers/crops_helper.rb' + - 'spec/features/crops/crop_detail_page_spec.rb' + +# Configuration parameters: EnforcedStyle. +# SupportedStyles: lowercase, uppercase +Naming/HeredocDelimiterCase: + Exclude: + - 'config/environments/production.rb' + +# Configuration parameters: Include. +# Include: db/migrate/*.rb +Rails/CreateTableWithTimestamps: + Exclude: + - 'db/migrate/20130214034838_add_members_roles_table.rb' + - 'db/migrate/20130507113915_add_orders_products_table.rb' + - 'db/migrate/20130531110729_add_photos_plantings_table.rb' + - 'db/migrate/20140905001730_add_harvests_photos_table.rb' + - 'db/migrate/20140928044231_add_crops_posts_table.rb' + - 'db/migrate/20150127043022_add_gardens_photos_table.rb' + - 'db/migrate/20150201052245_create_cms.rb' + - 'db/migrate/20161201154922_add_photos_seeds_table.rb' + - 'db/migrate/20171022032108_all_the_predictions.rb' + Rails/FilePath: Exclude: - 'spec/rails_helper.rb' +# Configuration parameters: Include. +# Include: app/models/**/*.rb +Rails/HasManyOrHasOneDependent: + Exclude: + - 'app/models/member.rb' + +# Configuration parameters: Include. +# Include: app/controllers/**/*.rb +Rails/LexicallyScopedActionFilter: + Exclude: + - 'app/controllers/alternate_names_controller.rb' + - 'app/controllers/comments_controller.rb' + - 'app/controllers/gardens_controller.rb' + - 'app/controllers/members_controller.rb' + - 'app/controllers/photos_controller.rb' + Rails/OutputSafety: Exclude: - 'app/helpers/application_helper.rb' @@ -40,7 +88,7 @@ Rails/SkipsModelValidations: Exclude: - 'db/seeds.rb' -# Configuration parameters: EnforcedStyle, SupportedStyles. +# Configuration parameters: EnforcedStyle. # SupportedStyles: strict, flexible Rails/TimeZone: Exclude: @@ -49,31 +97,52 @@ Rails/TimeZone: - 'spec/models/post_spec.rb' # Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. +# Configuration parameters: EnforcedStyle. # SupportedStyles: always, conditionals Style/AndOr: Exclude: - 'config/unicorn.rb' - 'lib/tasks/growstuff.rake' -# Configuration parameters: EnforcedStyle, SupportedStyles. +# Cop supports --auto-correct. +# Configuration parameters: AutoCorrect, EnforcedStyle. # SupportedStyles: nested, compact Style/ClassAndModuleChildren: Exclude: - 'lib/actions/oauth_signup_action.rb' - 'lib/haml/filters/escaped_markdown.rb' +Style/CommentedKeyword: + Exclude: + - 'lib/tasks/growstuff.rake' + - 'spec/models/crop_spec.rb' + - 'spec/models/photo_spec.rb' + - 'spec/models/planting_spec.rb' + +# Configuration parameters: EnforcedStyle. +# SupportedStyles: annotated, template, unannotated +Style/FormatStringToken: + Exclude: + - 'app/helpers/application_helper.rb' + - 'spec/helpers/application_helper_spec.rb' + Style/IdenticalConditionalBranches: Exclude: - 'app/controllers/follows_controller.rb' +Style/MixinUsage: + Exclude: + - 'bin/setup' + - 'bin/update' + - 'spec/rails_helper.rb' + # Cop supports --auto-correct. Style/MultilineIfModifier: Exclude: - 'spec/rails_helper.rb' # Cop supports --auto-correct. -# Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles. +# Configuration parameters: AutoCorrect, EnforcedStyle. # SupportedStyles: predicate, comparison Style/NumericPredicate: Exclude: @@ -83,7 +152,7 @@ Style/NumericPredicate: - 'lib/tasks/growstuff.rake' # Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes. +# Configuration parameters: EnforcedStyle, AllowInnerSlashes. # SupportedStyles: slashes, percent_r, mixed Style/RegexpLiteral: Exclude: @@ -94,7 +163,7 @@ Style/RegexpLiteral: - 'spec/views/posts/index.html.haml_spec.rb' # Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. +# Configuration parameters: EnforcedStyle. # SupportedStyles: single_quotes, double_quotes Style/StringLiteralsInInterpolation: Exclude: From 75e401162ee32ff999db9089ec1cc9e81a21803d Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 16:12:40 +1200 Subject: [PATCH 119/219] More dependent declared on relationships --- app/models/crop.rb | 2 +- app/models/forum.rb | 2 +- app/models/member.rb | 10 +++++----- app/models/plant_part.rb | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/models/crop.rb b/app/models/crop.rb index 3e8b3981b..e41343253 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -19,7 +19,7 @@ class Crop < ApplicationRecord belongs_to :creator, class_name: 'Member', optional: true belongs_to :requester, class_name: 'Member', optional: true belongs_to :parent, class_name: 'Crop', optional: true - has_many :varieties, class_name: 'Crop', foreign_key: 'parent_id' + has_many :varieties, class_name: 'Crop', foreign_key: 'parent_id', dependent: :nullify has_and_belongs_to_many :posts # rubocop:disable Rails/HasAndBelongsToMany ## diff --git a/app/models/forum.rb b/app/models/forum.rb index 2579876ad..7dad73bc2 100644 --- a/app/models/forum.rb +++ b/app/models/forum.rb @@ -3,7 +3,7 @@ class Forum < ApplicationRecord validates :name, presence: true friendly_id :name, use: %i(slugged finders) - has_many :posts + has_many :posts, dependent: :destroy belongs_to :owner, class_name: "Member" def to_s diff --git a/app/models/member.rb b/app/models/member.rb index e42d2f11c..655f3c5aa 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -180,9 +180,9 @@ class Member < ApplicationRecord confirmed_at && will_save_change_to_attribute?(:newsletter) end - def newsletter_subscribe(gb = Gibbon::API.new, testing = false) + def newsletter_subscribe(gibbon = Gibbon::API.new, testing = false) return true if Rails.env.test? && !testing - gb.lists.subscribe( + gibbon.lists.subscribe( id: Rails.application.config.newsletter_list_id, email: { email: email }, merge_vars: { login_name: login_name }, @@ -190,10 +190,10 @@ class Member < ApplicationRecord ) end - def newsletter_unsubscribe(gb = Gibbon::API.new, testing = false) + def newsletter_unsubscribe(gibbon = Gibbon::API.new, testing = false) return true if Rails.env.test? && !testing - gb.lists.unsubscribe(id: Rails.application.config.newsletter_list_id, - email: { email: email }) + gibbon.lists.unsubscribe(id: Rails.application.config.newsletter_list_id, + email: { email: email }) end def already_following?(member) diff --git a/app/models/plant_part.rb b/app/models/plant_part.rb index bbdbae8d2..4cebaf79d 100644 --- a/app/models/plant_part.rb +++ b/app/models/plant_part.rb @@ -2,7 +2,7 @@ class PlantPart < ApplicationRecord extend FriendlyId friendly_id :name, use: %i(slugged finders) - has_many :harvests + has_many :harvests, dependent: :destroy has_many :crops, -> { distinct }, through: :harvests def to_s From 59cca1b8cb32836cdff0c349a020976af7e228dc Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 16:18:46 +1200 Subject: [PATCH 120/219] Upgraded controller specs for rails 5 --- ..._controller_spec => crops_controller_spec.rb} | 6 +++--- .../charts/gardens_controller_spec.rb | 4 ++-- spec/controllers/crops_controller_spec.rb | 16 ---------------- spec/controllers/gardens_controller_spec.rb | 9 --------- spec/controllers/plantings_controller_spec.rb | 2 +- spec/controllers/seeds_controller_spec.rb | 2 +- 6 files changed, 7 insertions(+), 32 deletions(-) rename spec/controllers/charts/{crops_controller_spec => crops_controller_spec.rb} (64%) diff --git a/spec/controllers/charts/crops_controller_spec b/spec/controllers/charts/crops_controller_spec.rb similarity index 64% rename from spec/controllers/charts/crops_controller_spec rename to spec/controllers/charts/crops_controller_spec.rb index 5a4aa6548..f487d4d37 100644 --- a/spec/controllers/charts/crops_controller_spec +++ b/spec/controllers/charts/crops_controller_spec.rb @@ -4,15 +4,15 @@ describe Charts::CropsController do describe 'GET charts' do let(:crop) { FactoryBot.create :crop } describe 'sunniness' do - before { get :sunniness, crop_id: crop.to_param } + before { get :sunniness, params: { crop_id: crop.to_param } } it { expect(response).to be_success } end describe 'planted_from' do - before { get :planted_from, crop_id: crop.to_param } + before { get :planted_from, params: { crop_id: crop.to_param } } it { expect(response).to be_success } end describe 'harvested_for' do - before { get :harvested_for, crop_id: crop.to_param } + before { get :harvested_for, params: { crop_id: crop.to_param } } it { expect(response).to be_success } end end diff --git a/spec/controllers/charts/gardens_controller_spec.rb b/spec/controllers/charts/gardens_controller_spec.rb index daa50a811..4230328d9 100644 --- a/spec/controllers/charts/gardens_controller_spec.rb +++ b/spec/controllers/charts/gardens_controller_spec.rb @@ -7,7 +7,7 @@ describe Charts::GardensController do let(:garden) { FactoryBot.create :garden } context "when not signed in" do describe 'GET timeline' do - before { get :timeline, garden_id: garden.to_param } + before { get :timeline, params: { garden_id: garden.to_param } } it { expect(response).to be_success } end end @@ -16,7 +16,7 @@ describe Charts::GardensController do let!(:member) { FactoryBot.create(:member) } describe 'GET timeline' do - before { get :timeline, garden_id: garden.to_param } + before { get :timeline, params: { garden_id: garden.to_param } } it { expect(response).to be_success } end end diff --git a/spec/controllers/crops_controller_spec.rb b/spec/controllers/crops_controller_spec.rb index 9e54307bf..5b682f6c2 100644 --- a/spec/controllers/crops_controller_spec.rb +++ b/spec/controllers/crops_controller_spec.rb @@ -46,20 +46,4 @@ describe CropsController do it { expect(response.content_type).to eq("application/rss+xml") } end end - - describe 'GET charts' do - let(:crop) { FactoryBot.create :crop } - describe 'sunniness' do - before { get :sunniness, crop_id: crop.to_param } - it { expect(response).to be_success } - end - describe 'planted_from' do - before { get :planted_from, crop_id: crop.to_param } - it { expect(response).to be_success } - end - describe 'harvested_for' do - before { get :harvested_for, crop_id: crop.to_param } - it { expect(response).to be_success } - end - end end diff --git a/spec/controllers/gardens_controller_spec.rb b/spec/controllers/gardens_controller_spec.rb index 22476bec6..931453907 100644 --- a/spec/controllers/gardens_controller_spec.rb +++ b/spec/controllers/gardens_controller_spec.rb @@ -14,10 +14,6 @@ RSpec.describe GardensController, type: :controller do before { put :create, params: { garden: valid_params } } it { expect(response).to redirect_to(new_member_session_path) } end - describe 'GET timeline' do - before { get :timeline, garden_id: garden.to_param } - it { expect(response).to be_success } - end describe 'changing existing records' do before do allow(Garden).to receive(:find).and_return(:garden) @@ -46,11 +42,6 @@ RSpec.describe GardensController, type: :controller do let!(:member) { FactoryBot.create(:member) } - describe 'GET timeline' do - before { get :timeline, garden_id: garden.to_param } - it { expect(response).to be_success } - end - describe "for another member's garden" do let(:not_my_garden) { double('garden') } diff --git a/spec/controllers/plantings_controller_spec.rb b/spec/controllers/plantings_controller_spec.rb index 8567a3386..8a0d5c7dc 100644 --- a/spec/controllers/plantings_controller_spec.rb +++ b/spec/controllers/plantings_controller_spec.rb @@ -87,7 +87,7 @@ describe PlantingsController do context 'with parent seed' do let(:seed) { FactoryBot.create :seed, owner: member } - before { get :new, seed_id: seed.to_param } + before { get :new, params: { seed_id: seed.to_param } } it { expect(assigns(:seed)).to eq(seed) } end end diff --git a/spec/controllers/seeds_controller_spec.rb b/spec/controllers/seeds_controller_spec.rb index 3ff8051f1..0a5fe45b4 100644 --- a/spec/controllers/seeds_controller_spec.rb +++ b/spec/controllers/seeds_controller_spec.rb @@ -22,7 +22,7 @@ describe SeedsController do context 'with parent planting' do let(:planting) { FactoryBot.create :planting, owner: owner } - before { get :new, planting_id: planting.to_param } + before { get :new, params: { planting_id: planting.to_param } } it { expect(assigns(:planting)).to eq(planting) } end end From af0c9a490b3ea58d41fcdbb29964c6df40fbd6eb Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 2 Apr 2018 16:28:34 +1200 Subject: [PATCH 121/219] Redirect :back deprecated. updating follows controller --- app/controllers/follows_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/follows_controller.rb b/app/controllers/follows_controller.rb index 2c4b2daab..b97eb9e2b 100644 --- a/app/controllers/follows_controller.rb +++ b/app/controllers/follows_controller.rb @@ -9,10 +9,10 @@ class FollowsController < ApplicationController if @follow.save flash[:notice] = "Followed #{@follow.followed.login_name}" - redirect_to :back + redirect_back fallback_location: root_path else flash[:error] = "Already following or error while following." - redirect_to :back + redirect_back fallback_location: root_path end end From 90f8b9e042a31b4f882220b171608a04bb7a537d Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 28 Apr 2018 21:30:08 +1200 Subject: [PATCH 122/219] Fixed last crop spec for rails5 ugprade --- app/models/crop.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/crop.rb b/app/models/crop.rb index a099e8c64..84c69a30f 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -15,7 +15,7 @@ class Crop < ApplicationRecord has_many :seeds, dependent: :destroy has_many :harvests, dependent: :destroy has_many :photos, through: :plantings - has_many :plant_parts, -> { uniq.reorder("plant_parts.name") }, through: :harvests + has_many :plant_parts, -> { distinct.order("plant_parts.name") }, through: :harvests belongs_to :creator, class_name: 'Member', optional: true belongs_to :requester, class_name: 'Member', optional: true belongs_to :parent, class_name: 'Crop', optional: true From 9cfdb93aa3dd521225fe003048a1efe2321c75c8 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 28 Apr 2018 21:35:00 +1200 Subject: [PATCH 123/219] Remove some old currency methods from application helper Conflicts: app/helpers/application_helper.rb --- app/helpers/application_helper.rb | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index dad3eab2b..7388445d5 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,26 +1,9 @@ module ApplicationHelper - def price_in_dollars(price) - format('%.2f', price / 100.0) - end - - # 999 cents becomes 9.99 AUD -- for products/orders/etc - def price_with_currency(price) - format('%.2f %s', price / 100.0, Rails.application.config.currency) - end - def parse_date(str) str ||= '' # Date.parse barfs on nil str == '' ? nil : Date.parse(str) end - def forex_link(price) - pid = price_in_dollars(price) - currency = Rails.application.config.currency - link = "http://www.wolframalpha.com/input/?i=#{pid}+#{currency}" - - link_to "(convert)", link, target: "_blank", rel: "noopener noreferrer" - end - def build_alert_classes(alert_type = :info) classes = 'alert alert-dismissable ' case alert_type.to_sym From cd910e74bcf89a6ccaf6aed60021a1dd83961842 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 28 Apr 2018 22:05:50 +1200 Subject: [PATCH 124/219] Don't need to throw exception on planting save error --- app/controllers/plantings_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/plantings_controller.rb b/app/controllers/plantings_controller.rb index 192663c2e..fc3db888b 100644 --- a/app/controllers/plantings_controller.rb +++ b/app/controllers/plantings_controller.rb @@ -56,7 +56,7 @@ class PlantingsController < ApplicationController @planting = Planting.new(planting_params) @planting.owner = current_member @planting.crop = @planting.parent_seed.crop if @planting.parent_seed.present? - @planting.save! + @planting.save respond_with @planting end From 1bbf0a3c18ec283a1331f18f4301dc0f6d290cd9 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 28 Apr 2018 22:11:02 +1200 Subject: [PATCH 125/219] Simplifying show_inactive_tickbox_path method --- app/helpers/application_helper.rb | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 7388445d5..ca4bc9429 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -67,16 +67,12 @@ module ApplicationHelper def show_inactive_tickbox_path(type, owner, show_all) all = show_all ? '' : 1 if owner - if type == 'plantings' - plantings_by_owner_path(owner: owner.slug, all: all) - elsif type == 'gardens' - gardens_by_owner_path(owner: owner.slug, all: all) - end - elsif type == 'plantings' - plantings_path(all: all) - elsif type == 'gardens' - gardens_path(all: all) + return plantings_by_owner_path(owner: owner.slug, all: all) if type == 'plantings' + return gardens_by_owner_path(owner: owner.slug, all: all) if type == 'gardens' end + + return plantings_path(all: all) if type == 'plantings' + return gardens_path(all: all) if type == 'gardens' end def title(type, owner, crop, planting) From 4e6ffc1a63d06f246ff837ee1514a6b509ae05f4 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 28 Apr 2018 22:21:35 +1200 Subject: [PATCH 126/219] Removed spec for currency helpers --- spec/helpers/application_helper_spec.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index dcc5e6145..d1eae5cf6 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -1,11 +1,6 @@ require 'rails_helper' describe ApplicationHelper do - it "formats prices" do - price_in_dollars(999).should eq '9.99' - price_with_currency(999).should eq format('9.99 %s', Rails.application.config.currency) - end - it "parses dates" do parse_date(nil).should eq nil parse_date('').should eq nil From cc838e19b5239b62803503a3c48e1d7d93b54e20 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 10:59:34 +1200 Subject: [PATCH 127/219] Show a 404 page on record not found --- app/controllers/application_controller.rb | 6 ++++ app/views/errors/404.html | 34 +++++++++++++++++++++++ app/views/errors/422.html | 34 +++++++++++++++++++++++ app/views/errors/500.html | 33 ++++++++++++++++++++++ 4 files changed, 107 insertions(+) create mode 100644 app/views/errors/404.html create mode 100644 app/views/errors/422.html create mode 100644 app/views/errors/500.html diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3e6221d06..eb775ee4a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -6,6 +6,8 @@ class ApplicationController < ActionController::Base after_action :store_location before_action :set_locale + rescue_from ActiveRecord::RecordNotFound, with: :not_found + # CanCan error handling def store_location unless request.path.in?(["/members/sign_in", "/members/sign_up", @@ -17,6 +19,10 @@ class ApplicationController < ActionController::Base end end + def not_found + render file: 'app/views/errors/404', status: :not_found, layout: false + end + def after_sign_in_path_for(resource) stored_location_for(:member) || root_path end diff --git a/app/views/errors/404.html b/app/views/errors/404.html new file mode 100644 index 000000000..9345cf4a2 --- /dev/null +++ b/app/views/errors/404.html @@ -0,0 +1,34 @@ + + + + + + The page you were looking for doesn't exist (404) + + + + + +
+

The page you were looking for doesn't exist.

+

You may have mistyped the address or the page may have moved.

+
+ + diff --git a/app/views/errors/422.html b/app/views/errors/422.html new file mode 100644 index 000000000..afc8eb76b --- /dev/null +++ b/app/views/errors/422.html @@ -0,0 +1,34 @@ + + + + + + The change you wanted was rejected (422) + + + + + +
+

The change you wanted was rejected.

+

Maybe you tried to change something you didn't have access to.

+
+ + diff --git a/app/views/errors/500.html b/app/views/errors/500.html new file mode 100644 index 000000000..af1052613 --- /dev/null +++ b/app/views/errors/500.html @@ -0,0 +1,33 @@ + + + + + + We're sorry, but something went wrong (500) + + + + + +
+

We're sorry, but something went wrong.

+
+ + From 02c5571a9a10f880b92c13184126d7cfc0c5571b Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 11:38:15 +1200 Subject: [PATCH 128/219] Notifications need sender and reciever in factory --- spec/factories/notifications.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/factories/notifications.rb b/spec/factories/notifications.rb index b893fcc1b..8b6867a46 100644 --- a/spec/factories/notifications.rb +++ b/spec/factories/notifications.rb @@ -2,8 +2,8 @@ FactoryBot.define do factory :notification, aliases: [:message] do - sender - recipient + sender { FactoryBot.create :member } + recipient { FactoryBot.create :member } subject "MyString" body "MyText" read false From 6913233596b47102204860e916cc2a01bc021af3 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 11:39:22 +1200 Subject: [PATCH 129/219] Use factorybot in more tests --- spec/features/signin_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/features/signin_spec.rb b/spec/features/signin_spec.rb index acd9f5295..82bec2068 100644 --- a/spec/features/signin_spec.rb +++ b/spec/features/signin_spec.rb @@ -1,10 +1,10 @@ require 'rails_helper' feature "signin", js: true do - let(:member) { create :member } - let(:recipient) { create :member } - let(:wrangler) { create :crop_wrangling_member } - let(:notification) { create :notification } + let(:member) { FactoryBot.create :member } + let(:recipient) { FactoryBot.create :member } + let(:wrangler) { FactoryBot.create :crop_wrangling_member } + let(:notification) { FactoryBot.create :notification } def login fill_in 'Login', with: member.login_name From b898d83d214d12d466552e754846a71636a7f189 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 11:49:30 +1200 Subject: [PATCH 130/219] Fix up notification redirect spec --- app/controllers/notifications_controller.rb | 2 +- spec/features/signin_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 7c3c1f322..bbab48b43 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -20,7 +20,7 @@ class NotificationsController < ApplicationController def new @notification = Notification.new - @recipient = Member.find_by(id: params[:recipient_id]) + @recipient = Member.find_by(id: params[:recipient]) @subject = params[:subject] || "" end diff --git a/spec/features/signin_spec.rb b/spec/features/signin_spec.rb index 82bec2068..e992d14e7 100644 --- a/spec/features/signin_spec.rb +++ b/spec/features/signin_spec.rb @@ -4,7 +4,7 @@ feature "signin", js: true do let(:member) { FactoryBot.create :member } let(:recipient) { FactoryBot.create :member } let(:wrangler) { FactoryBot.create :crop_wrangling_member } - let(:notification) { FactoryBot.create :notification } + let(:notification) { FactoryBot.create :notification, recipient: recipient } def login fill_in 'Login', with: member.login_name @@ -56,7 +56,7 @@ feature "signin", js: true do end scenario "after signin, redirect to new notifications page" do - visit new_notification_path(recipient: recipient) + visit new_notification_path(recipient: recipient.id) expect(current_path).to eq new_member_session_path login expect(current_path).to eq new_notification_path From a221f10113663c25a679c525e42dc45cefcf7749 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 12:03:11 +1200 Subject: [PATCH 131/219] Move photos owner validation to models --- app/controllers/photos_controller.rb | 2 +- app/models/photographing.rb | 8 ++++++++ spec/controllers/photos_controller_spec.rb | 4 ++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb index 57b10e5d8..c9dd8886f 100644 --- a/app/controllers/photos_controller.rb +++ b/app/controllers/photos_controller.rb @@ -79,7 +79,7 @@ class PhotosController < ApplicationController raise "No item type provided" if item_type.nil? item_class = item_type.capitalize raise "Photos not supported" unless Photo::PHOTO_CAPABLE.include? item_class - item_class.constantize.find_by!(id: params[:id], owner_id: current_member.id) + item_class.constantize.find(params[:id]) end # diff --git a/app/models/photographing.rb b/app/models/photographing.rb index 5d1ab1073..e8999db43 100644 --- a/app/models/photographing.rb +++ b/app/models/photographing.rb @@ -2,6 +2,8 @@ class Photographing < ApplicationRecord belongs_to :photo belongs_to :photographable, polymorphic: true + validate :photo_and_item_have_same_owner + def self.item(item_id, item_type) find_by!(photographable_id: item_id, photographable_type: item_type).photographable end @@ -9,4 +11,10 @@ class Photographing < ApplicationRecord def item find_by!(photographable_id: photographable_id, photographable_type: photographable_type).photographable end + + private + + def photo_and_item_have_same_owner + errors.add(:photo, "must have same owner as item it links to") unless photographable.owner == photo.owner + end end diff --git a/spec/controllers/photos_controller_spec.rb b/spec/controllers/photos_controller_spec.rb index 41c43f572..a7cf6acb6 100644 --- a/spec/controllers/photos_controller_spec.rb +++ b/spec/controllers/photos_controller_spec.rb @@ -163,7 +163,7 @@ describe PhotosController do post :create, params: { photo: { flickr_photo_id: photo.flickr_photo_id }, type: "planting", id: another_planting.id } - end.to raise_error(ActiveRecord::RecordNotFound) + end.to raise_error(ActiveRecord::RecordInvalid) Photo.last.plantings.first.should_not eq another_planting end @@ -174,7 +174,7 @@ describe PhotosController do post :create, params: { photo: { flickr_photo_id: photo.flickr_photo_id }, type: "harvest", id: another_harvest.id } - end.to raise_error(ActiveRecord::RecordNotFound) + end.to raise_error(ActiveRecord::RecordInvalid) Photo.last.harvests.first.should_not eq another_harvest end end From 127b72b2790a01bdaf58827688b6284503736e2b Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 12:09:31 +1200 Subject: [PATCH 132/219] Updated spec to look for 404 page instead of exception --- spec/controllers/member_controller_spec.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/controllers/member_controller_spec.rb b/spec/controllers/member_controller_spec.rb index f4bab7ff8..8e6b91283 100644 --- a/spec/controllers/member_controller_spec.rb +++ b/spec/controllers/member_controller_spec.rb @@ -44,12 +44,14 @@ describe MembersController do end it "doesn't show completely nonsense members" do - -> { get :show, params: { id: 9999 } }.should raise_error(ActiveRecord::RecordNotFound) + get :show, params: { id: 9999 } + expect(response).to have_http_status(:not_found) end it "doesn't show unconfirmed members" do @member2 = FactoryBot.create(:unconfirmed_member) - -> { get :show, params: { id: @member2.id } }.should raise_error(ActiveRecord::RecordNotFound) + get :show, params: { id: @member2.id } + expect(response).to have_http_status(:not_found) end end From a590b89302dd65df53cec38d42b14cbe63cc2dd3 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 12:21:19 +1200 Subject: [PATCH 133/219] Photos in spec needs to have same owner as item they link to --- app/controllers/notifications_controller.rb | 2 +- .../photo_associations_controller.rb | 2 + app/models/photographing.rb | 2 +- app/models/planting.rb | 2 +- .../notifications_controller_spec.rb | 2 +- .../photo_associations_controller_spec.rb | 8 ++- spec/features/home/home_spec.rb | 4 +- spec/features/photos/show_photo_spec.rb | 6 +- spec/helpers/photos_helper_spec.rb | 15 ++-- spec/models/crop_spec.rb | 57 +++++++-------- spec/models/garden_spec.rb | 34 ++++----- spec/models/harvest_spec.rb | 49 +++++++------ spec/models/photo_spec.rb | 6 +- spec/models/planting_spec.rb | 71 ++++++++----------- spec/models/seed_spec.rb | 4 +- spec/views/crops/index.html.haml_spec.rb | 2 +- 16 files changed, 133 insertions(+), 133 deletions(-) diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index bbab48b43..7c3c1f322 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -20,7 +20,7 @@ class NotificationsController < ApplicationController def new @notification = Notification.new - @recipient = Member.find_by(id: params[:recipient]) + @recipient = Member.find_by(id: params[:recipient_id]) @subject = params[:subject] || "" end diff --git a/app/controllers/photo_associations_controller.rb b/app/controllers/photo_associations_controller.rb index 126aaf9b3..c63cca398 100644 --- a/app/controllers/photo_associations_controller.rb +++ b/app/controllers/photo_associations_controller.rb @@ -11,6 +11,8 @@ class PhotoAssociationsController < ApplicationController respond_with(@photo) end + private + def item_class params[:type].capitalize end diff --git a/app/models/photographing.rb b/app/models/photographing.rb index e8999db43..040817611 100644 --- a/app/models/photographing.rb +++ b/app/models/photographing.rb @@ -15,6 +15,6 @@ class Photographing < ApplicationRecord private def photo_and_item_have_same_owner - errors.add(:photo, "must have same owner as item it links to") unless photographable.owner == photo.owner + errors.add(:photo, "must have same owner as item it links to") unless photographable.owner_id == photo.owner_id end end diff --git a/app/models/planting.rb b/app/models/planting.rb index 144d3ee42..f6966d94f 100644 --- a/app/models/planting.rb +++ b/app/models/planting.rb @@ -29,7 +29,7 @@ class Planting < ApplicationRecord ## ## Scopes default_scope { joins(:owner) } # Ensures the owner still exists - scope :interesting, -> { has_photos.one_per_owner } + scope :interesting, -> { has_photos.one_per_owner.order(planted_at: :desc) } scope :recent, -> { order(created_at: :desc) } scope :one_per_owner, lambda { joins("JOIN members m ON (m.id=plantings.owner_id) diff --git a/spec/controllers/notifications_controller_spec.rb b/spec/controllers/notifications_controller_spec.rb index a7ed81ae5..30cf37ddd 100644 --- a/spec/controllers/notifications_controller_spec.rb +++ b/spec/controllers/notifications_controller_spec.rb @@ -76,7 +76,7 @@ describe NotificationsController do it "assigns a recipient" do @recipient = FactoryBot.create(:member) get :new, params: { recipient_id: @recipient.id } - assigns(:recipient).should be_an_instance_of(Member) + expect(assigns(:recipient)).to be_an_instance_of(Member) end end diff --git a/spec/controllers/photo_associations_controller_spec.rb b/spec/controllers/photo_associations_controller_spec.rb index bbac961fa..ffde9e1e3 100644 --- a/spec/controllers/photo_associations_controller_spec.rb +++ b/spec/controllers/photo_associations_controller_spec.rb @@ -24,7 +24,7 @@ describe PhotoAssociationsController do end describe "another member's harvest from another member's photo" do - let(:harvest) { FactoryBot.create :harvest } + let(:harvest) { FactoryBot.create :harvest, owner: photo.owner } let(:photo) { FactoryBot.create :photo } it do @@ -36,7 +36,11 @@ describe PhotoAssociationsController do end end.not_to change(photo.harvests, :count) end - it { expect { delete :destroy, params: valid_params }.to raise_error(ActiveRecord::RecordNotFound) } + + it do + delete :destroy, params: valid_params + expect(response).to have_http_response(:not_found) + end end end end diff --git a/spec/features/home/home_spec.rb b/spec/features/home/home_spec.rb index 4137b5e5a..ebbcb9fcb 100644 --- a/spec/features/home/home_spec.rb +++ b/spec/features/home/home_spec.rb @@ -2,8 +2,8 @@ require 'rails_helper' feature "home page" do let(:member) { FactoryBot.create :member } - # let(:seed_photo) { FactoryBot.create :photo } - let(:photo) { FactoryBot.create :photo } + + let(:photo) { FactoryBot.create :photo, owner: member } let(:crop) { FactoryBot.create :crop, created_at: 1.day.ago } let(:planting) { FactoryBot.create :planting, owner: member, crop: crop } diff --git a/spec/features/photos/show_photo_spec.rb b/spec/features/photos/show_photo_spec.rb index bac25b370..2fa9d7119 100644 --- a/spec/features/photos/show_photo_spec.rb +++ b/spec/features/photos/show_photo_spec.rb @@ -1,8 +1,6 @@ require 'rails_helper' feature "show photo page" do - let(:photo) { create :photo } - context "signed in member" do let(:member) { create :member } @@ -10,6 +8,7 @@ feature "show photo page" do context "linked to planting" do let(:planting) { create :planting } + let(:photo) { create :photo, owner: planting.owner } scenario "shows linkback to planting" do planting.photos << photo @@ -20,6 +19,7 @@ feature "show photo page" do end context "linked to harvest" do + let(:photo) { create :photo, owner: harvest.owner } let(:harvest) { create :harvest } scenario "shows linkback to harvest" do @@ -30,6 +30,7 @@ feature "show photo page" do end context "linked to garden" do + let(:photo) { create :photo, owner: garden.owner } let(:garden) { create :garden } scenario "shows linkback to garden" do @@ -40,6 +41,7 @@ feature "show photo page" do end context "linked to seed" do + let(:photo) { create :photo, owner: seed.owner } let(:seed) { create :seed } scenario "shows linkback to seed" do diff --git a/spec/helpers/photos_helper_spec.rb b/spec/helpers/photos_helper_spec.rb index 589cf8104..6a17ae4fd 100644 --- a/spec/helpers/photos_helper_spec.rb +++ b/spec/helpers/photos_helper_spec.rb @@ -4,13 +4,14 @@ describe PhotosHelper do let(:crop) { FactoryBot.create :crop } let(:garden) { FactoryBot.create :garden } - let(:garden_photo) { FactoryBot.create(:photo, thumbnail_url: 'garden.jpg') } - let(:planting) { FactoryBot.create :planting, crop: crop } - let(:planting_photo) { FactoryBot.create(:photo, thumbnail_url: 'planting.jpg') } - let(:harvest) { FactoryBot.create :harvest, crop: crop } - let(:harvest_photo) { FactoryBot.create(:photo, thumbnail_url: 'harvest.jpg') } - let(:seed) { FactoryBot.create :seed, crop: crop } - let(:seed_photo) { FactoryBot.create(:photo, thumbnail_url: 'seed.jpg') } + + let(:garden_photo) { FactoryBot.create(:photo, thumbnail_url: 'garden.jpg', owner: garden.owner) } + let(:planting) { FactoryBot.create :planting, crop: crop, owner: garden.owner } + let(:planting_photo) { FactoryBot.create(:photo, thumbnail_url: 'planting.jpg', owner: garden.owner) } + let(:harvest) { FactoryBot.create :harvest, crop: crop, owner: garden.owner } + let(:harvest_photo) { FactoryBot.create(:photo, thumbnail_url: 'harvest.jpg', owner: garden.owner) } + let(:seed) { FactoryBot.create :seed, crop: crop, owner: garden.owner } + let(:seed_photo) { FactoryBot.create(:photo, thumbnail_url: 'seed.jpg', owner: garden.owner) } describe "crops" do subject { crop_image_path(crop) } diff --git a/spec/models/crop_spec.rb b/spec/models/crop_spec.rb index 3f49a249d..415cf922a 100644 --- a/spec/models/crop_spec.rb +++ b/spec/models/crop_spec.rb @@ -55,26 +55,26 @@ describe Crop do end it "sorts by most plantings" do - Crop.popular.first.should eq maize + expect(Crop.popular.first).to eq maize FactoryBot.create_list(:planting, 10, crop: tomato) - Crop.popular.first.should eq tomato + expect(Crop.popular.first).to eq tomato end end it 'finds a default scientific name' do @crop = FactoryBot.create(:tomato) - @crop.default_scientific_name.should eq nil + expect(@crop.default_scientific_name).to eq nil @sn = FactoryBot.create(:solanum_lycopersicum, crop: @crop) @crop.reload - @crop.default_scientific_name.should eq @sn.name + expect(@crop.default_scientific_name).to eq @sn.name end it 'counts plantings' do @crop = FactoryBot.create(:tomato) - @crop.plantings.size.should eq 0 + expect(@crop.plantings.size).to eq 0 @planting = FactoryBot.create(:planting, crop: @crop) @crop.reload - @crop.plantings.size.should eq 1 + expect(@crop.plantings.size).to eq 1 end context "wikipedia url" do @@ -127,14 +127,14 @@ describe Crop do it 'has a crop hierarchy' do @tomato = FactoryBot.create(:tomato) @roma = FactoryBot.create(:roma, parent_id: @tomato.id) - @roma.parent.should eq @tomato - @tomato.varieties.should eq [@roma] + expect(@roma.parent).to eq @tomato + expect(@tomato.varieties).to eq [@roma] end it 'toplevel scope works' do @tomato = FactoryBot.create(:tomato) @roma = FactoryBot.create(:roma, parent_id: @tomato.id) - Crop.toplevel.should eq [@tomato] + expect(Crop.toplevel).to eq [@tomato] end end @@ -145,13 +145,13 @@ describe Crop do context 'with a planting photo' do before :each do @planting = FactoryBot.create(:planting, crop: @crop) - @photo = FactoryBot.create(:photo) + @photo = FactoryBot.create(:photo, owner: @planting.owner) @planting.photos << @photo end it 'has a default photo' do @crop.default_photo.should be_an_instance_of Photo - @crop.default_photo.id.should eq @photo.id + expect(@crop.default_photo.id).to eq @photo.id end it 'is found in has_photos scope' do @@ -162,31 +162,31 @@ describe Crop do context 'with a harvest photo' do before :each do @harvest = FactoryBot.create(:harvest, crop: @crop) - @photo = FactoryBot.create(:photo) + @photo = FactoryBot.create(:photo, owner: @harvest.owner) @harvest.photos << @photo end it 'has a default photo' do @crop.default_photo.should be_an_instance_of Photo - @crop.default_photo.id.should eq @photo.id + expect(@crop.default_photo.id).to eq @photo.id end context 'and planting photo' do before :each do @planting = FactoryBot.create(:planting, crop: @crop) - @planting_photo = FactoryBot.create(:photo) + @planting_photo = FactoryBot.create(:photo, owner: @planting.owner) @planting.photos << @planting_photo end it 'should prefer the planting photo' do - @crop.default_photo.id.should eq @planting_photo.id + expect(@crop.default_photo.id).to eq @planting_photo.id end end end context 'with no plantings or harvests' do it 'has no default photo' do - @crop.default_photo.should eq nil + expect(@crop.default_photo).to eq nil end end end @@ -285,14 +285,15 @@ describe Crop do let(:crop1_planting) { crop1.plantings.first } let(:crop2_planting) { crop2.plantings.first } + let(:member) { FactoryBot.create :member, login_name: 'pikachu' } describe 'lists interesting crops' do before do # they need 3+ plantings each to be interesting - FactoryBot.create_list(:planting, 3, crop: crop1) - FactoryBot.create_list(:planting, 3, crop: crop2) + FactoryBot.create_list(:planting, 3, crop: crop1, owner: member) + FactoryBot.create_list(:planting, 3, crop: crop2, owner: member) # crops need 3+ photos to be interesting - crop1_planting.photos = FactoryBot.create_list :photo, 3 - crop2_planting.photos = FactoryBot.create_list :photo, 3 + crop1_planting.photos = FactoryBot.create_list :photo, 3, owner: member + crop2_planting.photos = FactoryBot.create_list :photo, 3, owner: member end it { is_expected.to include crop1 } @@ -303,9 +304,9 @@ describe Crop do describe 'crops without plantings are not interesting' do before do # only crop1 has plantings - FactoryBot.create_list(:planting, 3, crop: crop1) + FactoryBot.create_list(:planting, 3, crop: crop1, owner: member) # ... and photos - crop1_planting.photos = FactoryBot.create_list(:photo, 3) + crop1_planting.photos = FactoryBot.create_list(:photo, 3, owner: member) end it { is_expected.to include crop1 } it { is_expected.not_to include crop2 } @@ -315,11 +316,11 @@ describe Crop do describe 'crops without photos are not interesting' do before do # both crops have plantings - FactoryBot.create_list(:planting, 3, crop: crop1) - FactoryBot.create_list(:planting, 3, crop: crop2) + FactoryBot.create_list(:planting, 3, crop: crop1, owner: member) + FactoryBot.create_list(:planting, 3, crop: crop2, owner: member) # but only crop1 has photos - crop1_planting.photos = FactoryBot.create_list(:photo, 3) + crop1_planting.photos = FactoryBot.create_list(:photo, 3, owner: member) end it { is_expected.to include crop1 } it { is_expected.not_to include crop2 } @@ -347,7 +348,7 @@ describe Crop do @pp1 = FactoryBot.create(:plant_part) @h1 = FactoryBot.create(:harvest, crop: @maize, plant_part: @pp1) @h2 = FactoryBot.create(:harvest, crop: @maize, plant_part: @pp1) - @maize.plant_parts.should eq [@pp1] + expect(@maize.plant_parts).to eq [@pp1] end context "search", :elasticsearch do @@ -356,10 +357,10 @@ describe Crop do before { sync_elasticsearch([mushroom]) } it "finds exact matches" do - Crop.search('mushroom').should eq [mushroom] + expect(Crop.search('mushroom')).to eq [mushroom] end it "finds approximate matches" do - Crop.search('mush').should eq [mushroom] + expect(Crop.search('mush')).to eq [mushroom] end it "doesn't find non-matches" do Crop.search('mush').should_not include @crop diff --git a/spec/models/garden_spec.rb b/spec/models/garden_spec.rb index baa8c001a..346e719b5 100644 --- a/spec/models/garden_spec.rb +++ b/spec/models/garden_spec.rb @@ -67,7 +67,7 @@ describe Garden do @p1 = FactoryBot.create(:planting, crop: tomato, garden: garden, owner: garden.owner) @p2 = FactoryBot.create(:planting, crop: maize, garden: garden, owner: garden.owner) - garden.featured_plantings.should eq [@p2, @p1] + expect(garden.featured_plantings).to eq [@p2, @p1] end it "should fetch most recent 4 featured plantings" do @@ -77,7 +77,7 @@ describe Garden do @p4 = FactoryBot.create(:planting, crop: apple, garden: garden, owner: garden.owner) @p5 = FactoryBot.create(:planting, crop: walnut, garden: garden, owner: garden.owner) - garden.featured_plantings.should eq [@p5, @p4, @p3, @p2] + expect(garden.featured_plantings).to eq [@p5, @p4, @p3, @p2] end it "should skip repeated plantings" do @@ -89,7 +89,7 @@ describe Garden do @p6 = FactoryBot.create(:planting, crop: apple, garden: garden, owner: garden.owner) @p7 = FactoryBot.create(:planting, crop: pear, garden: garden, owner: garden.owner) - garden.featured_plantings.should eq [@p7, @p6, @p5, @p3] + expect(garden.featured_plantings).to eq [@p7, @p6, @p5, @p3] end end @@ -97,10 +97,10 @@ describe Garden do garden = FactoryBot.create(:garden, owner: owner) @planting1 = FactoryBot.create(:planting, garden: garden, owner: garden.owner) @planting2 = FactoryBot.create(:planting, garden: garden, owner: garden.owner) - garden.plantings.size.should eq(2) + expect(garden.plantings.size).to eq(2) all = Planting.count garden.destroy - Planting.count.should eq(all - 2) + expect(Planting.count).to eq(all - 2) end context 'area' do @@ -157,7 +157,7 @@ describe Garden do it 'sets area unit to blank if area is blank' do garden = FactoryBot.build(:garden, area: '', area_unit: 'acre') garden.should be_valid - garden.area_unit.should eq nil + expect(garden.area_unit).to eq nil end end @@ -180,16 +180,16 @@ describe Garden do p1 = FactoryBot.create(:planting, garden: garden, owner: garden.owner) p2 = FactoryBot.create(:planting, garden: garden, owner: garden.owner) - p1.finished.should eq false - p2.finished.should eq false + expect(p1.finished).to eq false + expect(p2.finished).to eq false garden.active = false garden.save p1.reload - p1.finished.should eq true + expect(p1.finished).to eq true p2.reload - p2.finished.should eq true + expect(p2.finished).to eq true end it "doesn't mark the wrong plantings as finished" do @@ -204,23 +204,23 @@ describe Garden do # plantings in that garden should be "finished" p1.reload - p1.finished.should eq true + expect(p1.finished).to eq true # plantings in other gardens should not be. p2.reload - p2.finished.should eq false + expect(p2.finished).to eq false end context 'photos' do let(:garden) { FactoryBot.create(:garden) } - let(:photo) { FactoryBot.create(:photo) } + let(:photo) { FactoryBot.create(:photo, owner: garden.owner) } before do garden.photos << photo end it 'has a photo' do - garden.photos.first.should eq photo + expect(garden.photos.first).to eq photo end it 'deletes association with photos when photo is deleted' do @@ -230,13 +230,13 @@ describe Garden do end it 'has a default photo' do - garden.default_photo.should eq photo + expect(garden.default_photo).to eq photo end it 'chooses the most recent photo' do - @photo2 = FactoryBot.create(:photo) + @photo2 = FactoryBot.create(:photo, owner: garden.owner) garden.photos << @photo2 - garden.default_photo.should eq @photo2 + expect(garden.default_photo).to eq @photo2 end end diff --git a/spec/models/harvest_spec.rb b/spec/models/harvest_spec.rb index 1944c6365..455afeb9b 100644 --- a/spec/models/harvest_spec.rb +++ b/spec/models/harvest_spec.rb @@ -61,7 +61,7 @@ describe Harvest do it 'sets unit to blank if quantity is blank' do @harvest = FactoryBot.build(:harvest, quantity: '', unit: 'individual') @harvest.should be_valid - @harvest.unit.should eq nil + expect(@harvest.unit).to eq nil end end @@ -114,7 +114,7 @@ describe Harvest do it 'sets weight_unit to blank if quantity is blank' do @harvest = FactoryBot.build(:harvest, weight_quantity: '', weight_unit: 'kg') @harvest.should be_valid - @harvest.weight_unit.should eq nil + expect(@harvest.weight_unit).to eq nil end end @@ -122,19 +122,19 @@ describe Harvest do it 'converts from pounds' do @harvest = FactoryBot.create(:harvest, weight_quantity: 2, weight_unit: "lb") @harvest.should be_valid - @harvest.reload.si_weight.should eq 0.907 + expect(@harvest.reload.si_weight).to eq 0.907 end it 'converts from ounces' do @harvest = FactoryBot.create(:harvest, weight_quantity: 16, weight_unit: "oz") @harvest.should be_valid - @harvest.reload.si_weight.should eq 0.454 + expect(@harvest.reload.si_weight).to eq 0.454 end it 'leaves kg alone' do @harvest = FactoryBot.create(:harvest, weight_quantity: 2, weight_unit: "kg") @harvest.should be_valid - @harvest.reload.si_weight.should eq 2.0 + expect(@harvest.reload.si_weight).to eq 2.0 end end @@ -142,7 +142,7 @@ describe Harvest do it 'lists most recent harvests first' do @h1 = FactoryBot.create(:harvest, created_at: 1.day.ago) @h2 = FactoryBot.create(:harvest, created_at: 1.hour.ago) - Harvest.all.order(created_at: :desc).should eq [@h2, @h1] + expect(Harvest.all.order(created_at: :desc)).to eq [@h2, @h1] end end @@ -155,7 +155,7 @@ describe Harvest do unit: nil, weight_quantity: nil, weight_unit: nil) - @h.to_s.should eq "apricots" + expect(@h.to_s).to eq "apricots" end it "1 individual apricot" do @@ -164,7 +164,7 @@ describe Harvest do unit: 'individual', weight_quantity: nil, weight_unit: nil) - @h.to_s.should eq "1 individual apricot" + expect(@h.to_s).to eq "1 individual apricot" end it "10 individual apricots" do @@ -173,7 +173,7 @@ describe Harvest do unit: 'individual', weight_quantity: nil, weight_unit: nil) - @h.to_s.should eq "10 individual apricots" + expect(@h.to_s).to eq "10 individual apricots" end it "1 bushel of apricots" do @@ -182,7 +182,7 @@ describe Harvest do unit: 'bushel', weight_quantity: nil, weight_unit: nil) - @h.to_s.should eq "1 bushel of apricots" + expect(@h.to_s).to eq "1 bushel of apricots" end it "1.5 bushels of apricots" do @@ -191,7 +191,7 @@ describe Harvest do unit: 'bushel', weight_quantity: nil, weight_unit: nil) - @h.to_s.should eq "1.5 bushels of apricots" + expect(@h.to_s).to eq "1.5 bushels of apricots" end it "10 bushels of apricots" do @@ -200,7 +200,7 @@ describe Harvest do unit: 'bushel', weight_quantity: nil, weight_unit: nil) - @h.to_s.should eq "10 bushels of apricots" + expect(@h.to_s).to eq "10 bushels of apricots" end it "apricots weighing 1.2 kg" do @@ -209,7 +209,7 @@ describe Harvest do unit: nil, weight_quantity: 1.2, weight_unit: 'kg') - @h.to_s.should eq "apricots weighing 1.2 kg" + expect(@h.to_s).to eq "apricots weighing 1.2 kg" end it "10 bushels of apricots weighing 100 kg" do @@ -218,7 +218,7 @@ describe Harvest do unit: 'bushel', weight_quantity: 100, weight_unit: 'kg') - @h.to_s.should eq "10 bushels of apricots weighing 100 kg" + expect(@h.to_s).to eq "10 bushels of apricots weighing 100 kg" end end @@ -229,26 +229,25 @@ describe Harvest do context 'without a photo' do it 'should have no default photo' do - @harvest.default_photo.should eq nil + expect(@harvest.default_photo).to eq nil end context 'and with a crop(planting) photo' do before :each do - @photo = FactoryBot.create(:photo) @planting = FactoryBot.create(:planting, crop: @harvest.crop) + @photo = FactoryBot.create(:photo, owner: @planting.owner) @planting.photos << @photo end it 'should have a default photo' do - @harvest.default_photo.should eq @photo + expect(@harvest.default_photo).to eq @photo end end end context 'with a photo' do before do - @photo = FactoryBot.create(:photo) - + @photo = FactoryBot.create(:photo, owner: @harvest.owner) @harvest.photos << @photo end @@ -257,7 +256,7 @@ describe Harvest do end it 'has a photo' do - @harvest.photos.first.should eq @photo + expect(@harvest.photos.first).to eq @photo end it 'deletes association with photos when photo is deleted' do @@ -267,29 +266,29 @@ describe Harvest do end it 'has a default photo' do - @harvest.default_photo.should eq @photo + expect(@harvest.default_photo).to eq @photo end context 'and with a crop(planting) photo' do before :each do - @crop_photo = FactoryBot.create(:photo) @planting = FactoryBot.create(:planting, crop: @harvest.crop) + @crop_photo = FactoryBot.create(:photo, owner: @planting.owner) @planting.photos << @crop_photo end it 'should prefer the harvest photo' do - @harvest.default_photo.should eq @photo + expect(@harvest.default_photo).to eq @photo end end context 'and a second photo' do before :each do - @photo2 = FactoryBot.create(:photo) + @photo2 = FactoryBot.create(:photo, owner: @harvest.owner) @harvest.photos << @photo2 end it 'chooses the most recent photo' do - @harvest.default_photo.should eq @photo2 + expect(@harvest.default_photo).to eq @photo2 end end end diff --git a/spec/models/photo_spec.rb b/spec/models/photo_spec.rb index 5f07a3e29..0e2c45a11 100644 --- a/spec/models/photo_spec.rb +++ b/spec/models/photo_spec.rb @@ -5,9 +5,9 @@ describe Photo do let(:member) { FactoryBot.create(:member) } describe 'add/delete functionality' do - let(:planting) { FactoryBot.create(:planting) } - let(:harvest) { FactoryBot.create(:harvest) } - let(:garden) { FactoryBot.create(:garden) } + let(:planting) { FactoryBot.create(:planting, owner: member) } + let(:harvest) { FactoryBot.create(:harvest, owner: member) } + let(:garden) { FactoryBot.create(:garden, owner: member) } context "adds photos" do it 'to a planting' do diff --git a/spec/models/planting_spec.rb b/spec/models/planting_spec.rb index 3c76d6ed6..194967bbd 100644 --- a/spec/models/planting_spec.rb +++ b/spec/models/planting_spec.rb @@ -313,81 +313,72 @@ describe Planting do # be done on this side, not on the photos side context 'photos' do let(:planting) { FactoryBot.create(:planting) } - let(:photo) { FactoryBot.create(:photo) } - - before do - planting.photos << photo - end + let(:photo) { FactoryBot.create(:photo, owner_id: planting.owner_id) } + before { planting.photos << photo } it 'has a photo' do - planting.photos.first.should eq photo + expect(planting.photos.first).to eq photo end it 'is found in has_photos scope' do - Planting.has_photos.should include(planting) + expect(Planting.has_photos).to include(planting) end it 'deletes association with photos when photo is deleted' do photo.destroy planting.reload - planting.photos.should be_empty + expect(planting.photos).to be_empty end it 'has a default photo' do - planting.default_photo.should eq photo + expect(planting.default_photo).to eq photo end it 'chooses the most recent photo' do - @photo2 = FactoryBot.create(:photo) + @photo2 = FactoryBot.create(:photo, owner: planting.owner) planting.photos << @photo2 - planting.default_photo.should eq @photo2 + expect(planting.default_photo).to eq @photo2 end end context 'interesting plantings' do - it 'picks up interesting plantings' do - # plantings have members created implicitly for them - # each member is different, hence these are all interesting - @planting1 = FactoryBot.create(:planting, created_at: 5.days.ago) - @planting2 = FactoryBot.create(:planting, created_at: 4.days.ago) - @planting3 = FactoryBot.create(:planting, created_at: 3.days.ago) - @planting4 = FactoryBot.create(:planting, created_at: 2.days.ago) + describe 'picks up interesting plantings' do + before do + # plantings have members created implicitly for them + # each member is different, hence these are all interesting + @planting1 = FactoryBot.create(:planting, planted_at: 5.days.ago) + @planting2 = FactoryBot.create(:planting, planted_at: 4.days.ago) + @planting3 = FactoryBot.create(:planting, planted_at: 3.days.ago) + @planting4 = FactoryBot.create(:planting, planted_at: 2.days.ago) - # plantings need photos to be interesting - @photo = FactoryBot.create(:photo) - [@planting1, @planting2, @planting3, @planting4].each do |p| - p.photos << @photo - p.save + # plantings need photos to be interesting + [@planting1, @planting2, @planting3, @planting4].each do |p| + p.photos << FactoryBot.create(:photo, owner_id: p.owner_id) + p.save + end end - [ - @planting4, - @planting3, - @planting2, - @planting1 - ].each do |p| - Planting.interesting.should include p - end + it { expect(Planting.interesting).to eq([@planting4, @planting3, @planting2, @planting1]) } end context "default arguments" do it 'ignores plantings without photos' do # first, an interesting planting @planting = FactoryBot.create(:planting) - @planting.photos << FactoryBot.create(:photo) + @planting.photos << FactoryBot.create(:photo, owner: @planting.owner) @planting.save # this one doesn't have a photo @no_photo_planting = FactoryBot.create(:planting) - Planting.interesting.should include @planting - Planting.interesting.should_not include @no_photo_planting + expect(Planting.interesting).to include @planting + expect(Planting.interesting).not_to include @no_photo_planting end it 'ignores plantings with the same owner' do # this planting is older @planting1 = FactoryBot.create(:planting, created_at: 1.day.ago) - @planting1.photos << FactoryBot.create(:photo) + @planting1.photos << FactoryBot.create(:photo, owner_id: @planting1.owner_id) @planting1.save # this one is newer, and has the same owner, through the garden @@ -395,12 +386,12 @@ describe Planting do created_at: 1.minute.ago, garden: @planting1.garden, owner: @planting1.owner) - @planting2.photos << FactoryBot.create(:photo) + @planting2.photos << FactoryBot.create(:photo, owner: @planting2.owner) @planting2.save # result: the newer one is interesting, the older one isn't - Planting.interesting.should include @planting2 - Planting.interesting.should_not include @planting1 + expect(Planting.interesting).to include @planting2 + expect(Planting.interesting).not_to include @planting1 end end @@ -408,9 +399,9 @@ describe Planting do it "only returns the number asked for" do @plantings = FactoryBot.create_list(:planting, 10) @plantings.each do |p| - p.photos << FactoryBot.create(:photo, owner: planting.owner) + p.photos << FactoryBot.create(:photo, owner: p.owner) end - Planting.interesting.limit(3).count.should eq 3 + expect(Planting.interesting.limit(3).count).to eq 3 end end end # interesting plantings diff --git a/spec/models/seed_spec.rb b/spec/models/seed_spec.rb index 690e8c75f..03634ced4 100644 --- a/spec/models/seed_spec.rb +++ b/spec/models/seed_spec.rb @@ -153,7 +153,7 @@ describe Seed do context 'photos' do let(:seed) { FactoryBot.create :seed } - before { seed.photos << FactoryBot.create(:photo) } + before { seed.photos << FactoryBot.create(:photo, owner: seed.owner) } it 'is found in has_photos scope' do Seed.has_photos.should include(seed) end @@ -161,7 +161,7 @@ describe Seed do context 'ancestry' do let(:parent_planting) { FactoryBot.create :planting } - let(:seed) { FactoryBot.create :seed, parent_planting: parent_planting } + let(:seed) { FactoryBot.create :seed, parent_planting: parent_planting, owner: parent_planting.owner } it "seed has a parent planting" do expect(seed.parent_planting).to eq(parent_planting) end diff --git a/spec/views/crops/index.html.haml_spec.rb b/spec/views/crops/index.html.haml_spec.rb index 9f2985035..7d05d18db 100644 --- a/spec/views/crops/index.html.haml_spec.rb +++ b/spec/views/crops/index.html.haml_spec.rb @@ -17,7 +17,7 @@ describe "crops/index" do it "shows photos where available" do @planting = FactoryBot.create(:planting, crop: @tomato) - @photo = FactoryBot.create(:photo) + @photo = FactoryBot.create(:photo, owner: @planting.owner) @planting.photos << @photo render assert_select "img", src: @photo.thumbnail_url From 517cfa3acd8579dc22562bfb0af32f94434195ca Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 15:34:50 +1200 Subject: [PATCH 134/219] spec fixes --- spec/controllers/photo_associations_controller_spec.rb | 2 +- spec/features/signin_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/controllers/photo_associations_controller_spec.rb b/spec/controllers/photo_associations_controller_spec.rb index ffde9e1e3..5ed022218 100644 --- a/spec/controllers/photo_associations_controller_spec.rb +++ b/spec/controllers/photo_associations_controller_spec.rb @@ -39,7 +39,7 @@ describe PhotoAssociationsController do it do delete :destroy, params: valid_params - expect(response).to have_http_response(:not_found) + expect(response).to have_http_status(:not_found) end end end diff --git a/spec/features/signin_spec.rb b/spec/features/signin_spec.rb index e992d14e7..4f208f366 100644 --- a/spec/features/signin_spec.rb +++ b/spec/features/signin_spec.rb @@ -56,7 +56,7 @@ feature "signin", js: true do end scenario "after signin, redirect to new notifications page" do - visit new_notification_path(recipient: recipient.id) + visit new_notification_path(recipient_id: recipient.id) expect(current_path).to eq new_member_session_path login expect(current_path).to eq new_notification_path From 748da6c3e63105397510d2ad1d0660ab2e583cef Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 16:02:07 +1200 Subject: [PATCH 135/219] Comments from deleted members need to show --- app/models/comment.rb | 2 +- app/views/comments/_single.html.haml | 7 +++---- spec/features/members/deletion_spec.rb | 6 ++++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/models/comment.rb b/app/models/comment.rb index 59d7102ad..3eda14aa4 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,5 +1,5 @@ class Comment < ApplicationRecord - belongs_to :author, class_name: 'Member' + belongs_to :author, -> { with_deleted }, class_name: 'Member' belongs_to :post scope :post_order, -> { reorder("created_at ASC") } # for display on post page diff --git a/app/views/comments/_single.html.haml b/app/views/comments/_single.html.haml index 951df30bb..d3488f4f9 100644 --- a/app/views/comments/_single.html.haml +++ b/app/views/comments/_single.html.haml @@ -6,10 +6,10 @@ .col-md-11 .comment-meta Posted by - - if comment.author - = link_to comment.author.login_name, member_path(comment.author) - - else + - if comment.author.deleted? Member Deleted + - else + = link_to comment.author.login_name, member_path(comment.author) on = comment.created_at - if comment.updated_at > comment.created_at @@ -27,4 +27,3 @@ - if can? :destroy, comment = link_to 'Delete', comment, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-default btn-xs' - diff --git a/spec/features/members/deletion_spec.rb b/spec/features/members/deletion_spec.rb index 22b91a33b..3e5cb2e88 100644 --- a/spec/features/members/deletion_spec.rb +++ b/spec/features/members/deletion_spec.rb @@ -75,6 +75,10 @@ feature "member deletion" do logout end + describe 'member exists but is marked deleted' do + it { expect(Member.with_deleted.find(member.id)).to eq member } + end + scenario "removes plantings" do visit planting_path(planting) expect(page.status_code).to eq(404) @@ -108,6 +112,8 @@ feature "member deletion" do end scenario "replaces comments on others' posts with deletion note, leaving post intact" do + FactoryBot.create :comment, post: othermemberpost, author: member, body: 'i am deleting my account' + visit post_path(othermemberpost) expect(page).not_to have_content member.login_name expect(page).to have_content other_member.login_name From bcaf245e645676f6d1bb0b43a9afe3d57fa3037f Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 16:10:24 +1200 Subject: [PATCH 136/219] photos#new doesn't redirect, needs item --- spec/features/signin_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/signin_spec.rb b/spec/features/signin_spec.rb index 4f208f366..7f362abf3 100644 --- a/spec/features/signin_spec.rb +++ b/spec/features/signin_spec.rb @@ -48,7 +48,7 @@ feature "signin", js: true do end describe "redirects to what you were trying to do" do - %w(plantings harvests posts photos gardens seeds).each do |m| + %w(plantings harvests posts gardens seeds).each do |m| it_behaves_like "redirects to what you were trying to do" do let(:model_name) { m } end From d7cc219569e087e69d212cf7d59cb56b66764cab Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 16:16:55 +1200 Subject: [PATCH 137/219] Unique login_name in member factory --- spec/factories/member.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/spec/factories/member.rb b/spec/factories/member.rb index ca63df4e2..d1b34a487 100644 --- a/spec/factories/member.rb +++ b/spec/factories/member.rb @@ -1,9 +1,7 @@ FactoryBot.define do - sequence(:email) { |n| "member#{n}@example.com" } - sequence(:login_name) { |n| "member#{n}" } - factory :member, aliases: %i(author owner sender recipient creator) do - login_name { "#{Faker::Name.first_name}_#{rand(1..1000)}" } + sequence(:login_name) { |n| "member#{n}" } + # login_name { "#{Faker::Internet.user_name}_#{rand(1..1000)}" } password 'password1' email { Faker::Internet.unique.email } tos_agreement true From 05bfbda5f56e1077c79b100ff6ee712ad1e98928 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 16:18:49 +1200 Subject: [PATCH 138/219] Rubocop style fix ups --- app/controllers/application_controller.rb | 4 ++-- app/controllers/passwords_controller.rb | 2 +- app/controllers/registrations_controller.rb | 2 +- app/validators/approved_validator.rb | 2 +- bin/setup | 1 - bin/yarn | 2 +- config/unicorn.rb | 4 ++-- 7 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index eb775ee4a..9ef6783e5 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -23,11 +23,11 @@ class ApplicationController < ActionController::Base render file: 'app/views/errors/404', status: :not_found, layout: false end - def after_sign_in_path_for(resource) + def after_sign_in_path_for(_resource) stored_location_for(:member) || root_path end - def after_sign_out_path_for(resource_or_scope) + def after_sign_out_path_for(_resource_or_scope) request.referer end diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb index c14a02b40..fb6852ea9 100644 --- a/app/controllers/passwords_controller.rb +++ b/app/controllers/passwords_controller.rb @@ -1,7 +1,7 @@ class PasswordsController < Devise::PasswordsController protected - def after_resetting_password_path_for(resource) + def after_resetting_password_path_for(_resource) root_path end end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 72e92b9fd..b9ed73dfe 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -46,7 +46,7 @@ class RegistrationsController < Devise::RegistrationsController end # check if we need the current password to update fields -def needs_password?(member, params) +def needs_password?(_member, params) params[:member][:password].present? || params[:member][:password_confirmation].present? end diff --git a/app/validators/approved_validator.rb b/app/validators/approved_validator.rb index e845b9529..8a77fa2da 100644 --- a/app/validators/approved_validator.rb +++ b/app/validators/approved_validator.rb @@ -1,5 +1,5 @@ class ApprovedValidator < ActiveModel::EachValidator - def validate_each(record, attribute, value) + def validate_each(record, attribute, _value) record.errors[attribute] << (options[:message] || 'must be approved') unless record.crop.try(:approved?) end end diff --git a/bin/setup b/bin/setup index d5163f8c1..b2293a35a 100755 --- a/bin/setup +++ b/bin/setup @@ -21,7 +21,6 @@ chdir APP_ROOT do # Install JavaScript dependencies if using Yarn # system('bin/yarn') - # puts "\n== Copying sample files ==" # unless File.exist?('config/database.yml') # cp 'config/database.yml.sample', 'config/database.yml' diff --git a/bin/yarn b/bin/yarn index ad111a882..4d2c50e52 100755 --- a/bin/yarn +++ b/bin/yarn @@ -2,7 +2,7 @@ VENDOR_PATH = File.expand_path('..', __dir__) Dir.chdir(VENDOR_PATH) do begin - exec "yarnpkg #{ARGV.join(" ")}" + exec "yarnpkg #{ARGV.join(' ')}" rescue Errno::ENOENT warn "Yarn executable was not detected in the system." warn "Download Yarn at https://yarnpkg.com/en/docs/install" diff --git a/config/unicorn.rb b/config/unicorn.rb index 6a63af23f..fa76bae3a 100644 --- a/config/unicorn.rb +++ b/config/unicorn.rb @@ -9,7 +9,7 @@ before_fork do |_server, _worker| Process.kill 'QUIT', Process.pid end - defined?(ActiveRecord::Base) and + defined?(ActiveRecord::Base) && ActiveRecord::Base.connection.disconnect! end @@ -18,6 +18,6 @@ after_fork do |_server, _worker| puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to sent QUIT' end - defined?(ActiveRecord::Base) and + defined?(ActiveRecord::Base) && ActiveRecord::Base.establish_connection end From f58c6c8f54523cbc0e11f208da40f9667222eded Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 16:20:39 +1200 Subject: [PATCH 139/219] Regen rubocop todo --- .rubocop_todo.yml | 45 ++------------------------------------------- 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 99782950a..1bfd8efc6 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,16 +1,11 @@ # This configuration was generated by -# `rubocop --auto-gen-config --no-offense-counts` -# on 2018-04-02 16:07:34 +1200 using RuboCop version 0.54.0. +# `rubocop --auto-gen-config --no-offense-counts --no-auto-gen-timestamp` +# using RuboCop version 0.55.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Cop supports --auto-correct. -Layout/EmptyLines: - Exclude: - - 'bin/setup' - Lint/HandleExceptions: Exclude: - 'lib/tasks/testing.rake' @@ -22,15 +17,6 @@ Lint/MissingCopEnableDirective: - 'config/compass.rb' - 'config/initializers/devise.rb' -# Cop supports --auto-correct. -# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods. -Lint/UnusedMethodArgument: - Exclude: - - 'app/controllers/application_controller.rb' - - 'app/controllers/passwords_controller.rb' - - 'app/controllers/registrations_controller.rb' - - 'app/validators/approved_validator.rb' - Lint/UriEscapeUnescape: Exclude: - 'app/helpers/crops_helper.rb' @@ -96,14 +82,6 @@ Rails/TimeZone: - 'spec/factories/post.rb' - 'spec/models/post_spec.rb' -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle. -# SupportedStyles: always, conditionals -Style/AndOr: - Exclude: - - 'config/unicorn.rb' - - 'lib/tasks/growstuff.rake' - # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, EnforcedStyle. # SupportedStyles: nested, compact @@ -119,13 +97,6 @@ Style/CommentedKeyword: - 'spec/models/photo_spec.rb' - 'spec/models/planting_spec.rb' -# Configuration parameters: EnforcedStyle. -# SupportedStyles: annotated, template, unannotated -Style/FormatStringToken: - Exclude: - - 'app/helpers/application_helper.rb' - - 'spec/helpers/application_helper_spec.rb' - Style/IdenticalConditionalBranches: Exclude: - 'app/controllers/follows_controller.rb' @@ -136,11 +107,6 @@ Style/MixinUsage: - 'bin/update' - 'spec/rails_helper.rb' -# Cop supports --auto-correct. -Style/MultilineIfModifier: - Exclude: - - 'spec/rails_helper.rb' - # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, EnforcedStyle. # SupportedStyles: predicate, comparison @@ -161,10 +127,3 @@ Style/RegexpLiteral: - 'spec/views/devise/registrations/edit_spec.rb' - 'spec/views/members/index.html.haml_spec.rb' - 'spec/views/posts/index.html.haml_spec.rb' - -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle. -# SupportedStyles: single_quotes, double_quotes -Style/StringLiteralsInInterpolation: - Exclude: - - 'bin/yarn' From 4d937a1f91a2e99dbd270500fe98d1a9b5af52e9 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 16:29:38 +1200 Subject: [PATCH 140/219] Restoring Comfy mexican sofa --- Gemfile | 2 +- Gemfile.lock | 48 ++++++++++++++++++++++++++++- app/views/layouts/_footer.html.haml | 6 ++-- config/routes.rb | 2 +- 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index cecb3d65e..68925a470 100644 --- a/Gemfile +++ b/Gemfile @@ -42,7 +42,7 @@ gem 'pg', '< 1.0.0' # Upstream bug, see https://github.com/Growst gem 'ruby-units' # for unit conversion gem 'unicorn' # http server -# gem 'comfortable_mexican_sofa' # content management system +gem 'comfortable_mexican_sofa', git: 'https://github.com/comfy/comfortable-mexican-sofa', branch: 'rails5.1' gem 'bootstrap-kaminari-views' # bootstrap views for kaminari gem 'kaminari' # pagination diff --git a/Gemfile.lock b/Gemfile.lock index 3854a293b..f8dd95732 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,24 @@ +GIT + remote: https://github.com/comfy/comfortable-mexican-sofa + revision: dca0c7c3669872a00722bb57d3ff0f4995dc4f09 + branch: rails5.1 + specs: + comfortable_mexican_sofa (1.12.10) + active_link_to (>= 1.0.0) + bootstrap-sass (>= 3.2.0) + bootstrap_form (>= 2.2.0) + codemirror-rails (>= 3.0.0) + coffee-rails (>= 3.1.0) + haml-rails (>= 0.3.0) + jquery-rails (>= 3.0.0) + jquery-ui-rails (>= 5.0.0) + kramdown (>= 1.0.0) + paperclip (>= 4.0.0) + plupload-rails (>= 1.2.1) + rails (>= 5.0.0, < 5.2) + rails-i18n (>= 4.0.0) + sass-rails (>= 4.0.3) + GEM remote: https://rubygems.org/ remote: https://rails-assets.org/ @@ -25,6 +46,9 @@ GEM erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) + active_link_to (1.0.5) + actionpack + addressable active_median (0.1.4) activerecord active_utils (3.3.11) @@ -65,6 +89,7 @@ GEM bootstrap-sass (3.3.7) autoprefixer-rails (>= 5.2.1) sass (>= 3.3.4) + bootstrap_form (2.7.0) builder (3.2.3) bullet (5.7.5) activesupport (>= 3.0.0) @@ -87,9 +112,12 @@ GEM chartkick (2.3.4) childprocess (0.9.0) ffi (~> 1.0, >= 1.0.11) + climate_control (0.2.0) cliver (0.3.2) codeclimate-test-reporter (1.0.8) simplecov (<= 0.13) + codemirror-rails (5.16.0) + railties (>= 3.0, < 6.0) coderay (1.1.2) coffee-rails (4.2.2) coffee-script (>= 2.2.0) @@ -240,6 +268,7 @@ GEM kaminari-core (= 1.1.1) kaminari-core (1.1.1) kgio (2.11.2) + kramdown (1.17.0) launchy (2.4.3) addressable (~> 2.3) leaflet-rails (1.3.1) @@ -257,6 +286,10 @@ GEM mini_mime (>= 0.1.1) memcachier (0.0.2) method_source (0.9.0) + mime-types (3.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2016.0521) + mimemagic (0.3.2) mini_mime (1.0.0) mini_portile2 (2.3.0) minitest (5.11.3) @@ -293,6 +326,12 @@ GEM omniauth-oauth (~> 1.1) rack orm_adapter (0.5.0) + paperclip (6.0.0) + activemodel (>= 4.2.0) + activesupport (>= 4.2.0) + mime-types + mimemagic (~> 0.3.0) + terrapin (~> 0.6.0) parallel (1.12.1) paranoia (2.4.1) activerecord (>= 4.0, < 5.3) @@ -302,6 +341,8 @@ GEM platform-api (2.1.0) heroics (~> 0.0.23) moneta (~> 0.8.1) + plupload-rails (1.2.1) + rails (>= 3.1) poltergeist (1.17.0) capybara (~> 2.1) cliver (~> 0.3.1) @@ -337,6 +378,9 @@ GEM nokogiri (>= 1.6) rails-html-sanitizer (1.0.4) loofah (~> 2.2, >= 2.2.2) + rails-i18n (5.1.1) + i18n (>= 0.7, < 2) + railties (>= 5.0, < 6) rails_12factor (0.0.3) rails_serve_static_assets rails_stdout_logging @@ -432,6 +476,8 @@ GEM tins (~> 1.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) + terrapin (0.6.0) + climate_control (>= 0.0.3, < 1.0) thor (0.19.4) thread (0.2.2) thread_safe (0.3.6) @@ -484,6 +530,7 @@ DEPENDENCIES chartkick codeclimate-test-reporter coffee-rails + comfortable_mexican_sofa! coveralls csv_shaper dalli @@ -548,7 +595,6 @@ DEPENDENCIES will_paginate xmlrpc - RUBY VERSION ruby 2.4.1p111 diff --git a/app/views/layouts/_footer.html.haml b/app/views/layouts/_footer.html.haml index b05b164d4..dcff23dd6 100644 --- a/app/views/layouts/_footer.html.haml +++ b/app/views/layouts/_footer.html.haml @@ -2,11 +2,11 @@ .container .row .col-md-4#footer1 - -# != cms_snippet_content(:footer1) + = cms_snippet_content(:footer1) .col-md-4#footer2 - -# != cms_snippet_content(:footer2) + = cms_snippet_content(:footer2) .col-md-4#footer3 - -# != cms_snippet_content(:footer3) + = cms_snippet_content(:footer3) %div{ style: "float: right;" } %a{ href: "http://opendefinition.org/ossd/" } %img{ src: "http://assets.okfn.org/images/ok_buttons/os_80x15_blue.png", alt: "" } diff --git a/config/routes.rb b/config/routes.rb index 046c1b261..e884b4d69 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -106,5 +106,5 @@ Rails.application.routes.draw do get '/.well-known/acme-challenge/:id' => 'pages#letsencrypt' # CMS stuff -- must remain LAST - # comfy_route :cms, path: '/', sitemap: false + comfy_route :cms, path: '/', sitemap: false end From 4be4e96a9f986ddce2485202a9e240077e52491c Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 16:30:38 +1200 Subject: [PATCH 141/219] Fixed or => || --- lib/tasks/growstuff.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/growstuff.rake b/lib/tasks/growstuff.rake index e6ec0650d..9fe5202d3 100644 --- a/lib/tasks/growstuff.rake +++ b/lib/tasks/growstuff.rake @@ -29,7 +29,7 @@ namespace :growstuff do task import_crops: :environment do require 'csv' - @file = ENV['file'] or raise "Usage: rake growstuff:import_crops file=file.csv" + (@file = ENV['file']) || raise("Usage: rake growstuff:import_crops file=file.csv") puts "Loading crops from #{@file}..." CSV.foreach(@file) do |row| From fd405ccad0fac4ed11af11c3d2cdd29b8d0f5092 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 16:34:00 +1200 Subject: [PATCH 142/219] Added back link to CMS --- app/views/admin/index.html.haml | 2 +- config/routes.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/index.html.haml b/app/views/admin/index.html.haml index f82c5c336..e7b164280 100644 --- a/app/views/admin/index.html.haml +++ b/app/views/admin/index.html.haml @@ -8,7 +8,7 @@ %ul#site_admin %li= link_to "Roles", roles_path %li= link_to "Forums", forums_path - -# %li= link_to "CMS", comfy_admin_cms_path + %li= link_to "CMS", comfy_admin_cms_path .col-md-4 %h2 Crop data admin diff --git a/config/routes.rb b/config/routes.rb index e884b4d69..a43de3f0c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -83,7 +83,7 @@ Rails.application.routes.draw do get 'auth/:provider/callback' => 'authentications#create' get 'members/auth/:provider/callback' => 'authentications#create' - # comfy_route :cms_admin, path: '/admin/cms' + comfy_route :cms_admin, path: '/admin/cms' namespace :admin do resources :members end From 7737f41b7f0579e1ced53d4e824ace77ea5afb95 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 16:39:51 +1200 Subject: [PATCH 143/219] Code style fixup --- spec/rails_helper.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 862b0316d..ec3f6af77 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -104,12 +104,14 @@ RSpec.configure do |config| # Prevent Poltergeist from fetching external URLs during feature tests config.before(:each, js: true) do - page.driver.browser.url_blacklist = [ - 'gravatar.com', - 'mapbox.com', - 'okfn.org', - 'googlecode.com' - ] if page.driver.browser.respond_to?(:url_blacklist) + if page.driver.browser.respond_to?(:url_blacklist) + page.driver.browser.url_blacklist = [ + 'gravatar.com', + 'mapbox.com', + 'okfn.org', + 'googlecode.com' + ] + end page.driver.browser.manage.window.maximize if page.driver.browser.respond_to?(:manage) end From f31ff5d508c27d9b195352088af73edf394d4e9f Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 16:42:37 +1200 Subject: [PATCH 144/219] Removed commented out gems --- Gemfile | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Gemfile b/Gemfile index 68925a470..1cbd29839 100644 --- a/Gemfile +++ b/Gemfile @@ -82,7 +82,6 @@ gem "chartkick" # client for Elasticsearch. Elasticsearch is a flexible # and powerful, distributed, real-time search and analytics engine. # An example of the use in the project is fuzzy crop search. - # Project does not use semver, so we want to be in sync with the version of # elasticsearch we use # See https://github.com/elastic/elasticsearch-ruby#compatibility @@ -114,11 +113,6 @@ group :development do gem 'better_errors' gem 'letter_opener' gem 'listen' - # gem 'binding_of_caller' - # gem 'guard' - # gem 'guard-rspec' - # gem 'pry' - # gem 'quiet_assets' end group :development, :test do From a816ec2bbabfeb8cb9b800d9d5dc8622598d9ce8 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 16:44:51 +1200 Subject: [PATCH 145/219] Fix up footer --- app/views/layouts/_footer.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/layouts/_footer.html.haml b/app/views/layouts/_footer.html.haml index dcff23dd6..e004bf69a 100644 --- a/app/views/layouts/_footer.html.haml +++ b/app/views/layouts/_footer.html.haml @@ -2,11 +2,11 @@ .container .row .col-md-4#footer1 - = cms_snippet_content(:footer1) + != cms_snippet_content(:footer1) .col-md-4#footer2 - = cms_snippet_content(:footer2) + != cms_snippet_content(:footer2) .col-md-4#footer3 - = cms_snippet_content(:footer3) + != cms_snippet_content(:footer3) %div{ style: "float: right;" } %a{ href: "http://opendefinition.org/ossd/" } %img{ src: "http://assets.okfn.org/images/ok_buttons/os_80x15_blue.png", alt: "" } From 1f6d76445e6f487054f54d73daaae5e40ee19f83 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 16:49:00 +1200 Subject: [PATCH 146/219] Added back CMS initialiser --- .../initializers/comfortable_mexican_sofa.rb | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 config/initializers/comfortable_mexican_sofa.rb diff --git a/config/initializers/comfortable_mexican_sofa.rb b/config/initializers/comfortable_mexican_sofa.rb new file mode 100644 index 000000000..1f47ac973 --- /dev/null +++ b/config/initializers/comfortable_mexican_sofa.rb @@ -0,0 +1,99 @@ +ComfortableMexicanSofa.configure do |config| + # Title of the admin area + # config.cms_title = 'ComfortableMexicanSofa CMS Engine' + + # Controller that is inherited from CmsAdmin::BaseController + # config.base_controller = 'ApplicationController' + + # Module responsible for authentication. You can replace it with your own. + # It simply needs to have #authenticate method. See http_auth.rb for reference. + config.admin_auth = 'CmsDeviseAuth' + + # Module responsible for authorization on admin side. It should have #authorize + # method that returns true or false based on params and loaded instance + # variables available for a given controller. + # config.admin_authorization = 'ComfyAdminAuthorization' + + # Module responsible for public authentication. Similar to the above. You also + # will have access to @cms_site, @cms_layout, @cms_page so you can use them in + # your logic. Default module doesn't do anything. + # config.public_auth = 'ComfyPublicAuthentication' + + # When arriving at /cms-admin you may chose to redirect to arbirtary path, + # for example '/cms-admin/users' + # config.admin_route_redirect = '' + + # File uploads use Paperclip and can support filesystem or s3 uploads. Override + # the upload method and appropriate settings based on Paperclip. For S3 see: + # http://rdoc.info/gems/paperclip/2.3.8/Paperclip/Storage/S3, and for + # filesystem see: http://rdoc.info/gems/paperclip/2.3.8/Paperclip/Storage/Filesystem + # If you are using S3 and HTTPS, pass :s3_protocol => '' to have URLs that use the protocol of the page + # config.upload_file_options = {:url => '/system/:class/:id/:attachment/:style/:filename'} + + # Sofa allows you to setup entire site from files. Database is updated with each + # request (if necessary). Please note that database entries are destroyed if there's + # no corresponding file. Fixtures are disabled by default. + # config.enable_fixtures = false + + # Path where fixtures can be located. + # config.fixtures_path = File.expand_path('db/cms_fixtures', Rails.root) + + # Importing fixtures into Database + # To load fixtures into the database just run this rake task: + # local: $ rake comfortable_mexican_sofa:fixtures:import FROM=example.local TO=localhost + # Heroku: $ heroku run rake comfortable_mexican_sofa:fixtures:import FROM=example.local TO=yourapp.herokuapp.com + # From indicates folder the fixtures are in and to is the Site hostname you have defined in the database. + + # Exporting fixtures into Files + # If you need to dump database contents into fixture files run: + # local: $ rake comfortable_mexican_sofa:fixtures:export FROM=localhost TO=example.local + # Heroku: $ heroku run rake comfortable_mexican_sofa:fixtures:export FROM=yourapp.herokuapp.com TO=example.local + # This will create example.local folder and dump all content from example.com Site. + + # Content for Layouts, Pages and Snippets has a revision history. You can revert + # a previous version using this system. You can control how many revisions per + # object you want to keep. Set it to 0 if you wish to turn this feature off. + # config.revisions_limit = 25 + + # Locale definitions. If you want to define your own locale merge + # {:locale => 'Locale Title'} with this. + # config.locales = {:en => 'English', :es => 'Español'} + + # Admin interface will respect the locale of the site being managed. However you can + # force it to English by setting this to `:en` + # config.admin_locale = nil + + # A class that is included as a sweeper to admin base controller if it's set + # config.admin_cache_sweeper = nil + + # By default you cannot have irb code inside your layouts/pages/snippets. + # Generally this is to prevent putting something like this: + # <% User.delete_all %> but if you really want to allow it... + # config.allow_irb = false + + # Whitelist of all helper methods that can be used via {{cms:helper}} tag. By default + # all helpers are allowed except `eval`, `send`, `call` and few others. Empty array + # will prevent rendering of all helpers. + # config.allowed_helpers = nil + + # Whitelist of partials paths that can be used via {{cms:partial}} tag. All partials + # are accessible by default. Empty array will prevent rendering of all partials. + # config.allowed_partials = nil + + # Site aliases, if you want to have aliases for your site. Good for harmonizing + # production env with dev/testing envs. + # e.g. config.hostname_aliases = {'host.com' => 'host.inv', 'host_a.com' => ['host.lvh.me', 'host.dev']} + # Default is nil (not used) + # config.hostname_aliases = nil + + # Reveal partials that can be overwritten in the admin area. + # Default is false. + # config.reveal_cms_partials = false +end + +module CmsDeviseAuth + def authenticate + return if current_member&.role?(:admin) + redirect_to root_path, alert: 'Permission denied. Please sign in as an admin user to use the CMS admin area.' + end +end From cf72bdc57da7eea76978f511a21756555573c623 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 16:49:41 +1200 Subject: [PATCH 147/219] Reinstate json response config --- config/initializers/wrap_parameters.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb index bbfc3961b..bffab6c85 100644 --- a/config/initializers/wrap_parameters.rb +++ b/config/initializers/wrap_parameters.rb @@ -9,6 +9,6 @@ ActiveSupport.on_load(:action_controller) do end # To enable root element in JSON for ActiveRecord objects. -# ActiveSupport.on_load(:active_record) do -# self.include_root_in_json = true -# end +ActiveSupport.on_load(:active_record) do + self.include_root_in_json = false +end From 3399a794b2f739702c148015db5a5dbb44559efb Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 16:55:29 +1200 Subject: [PATCH 148/219] Ignore one non-ascii comment --- config/initializers/comfortable_mexican_sofa.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/initializers/comfortable_mexican_sofa.rb b/config/initializers/comfortable_mexican_sofa.rb index 1f47ac973..314bc21fe 100644 --- a/config/initializers/comfortable_mexican_sofa.rb +++ b/config/initializers/comfortable_mexican_sofa.rb @@ -55,9 +55,11 @@ ComfortableMexicanSofa.configure do |config| # object you want to keep. Set it to 0 if you wish to turn this feature off. # config.revisions_limit = 25 + # rubocop:disable Style/AsciiComments # Locale definitions. If you want to define your own locale merge # {:locale => 'Locale Title'} with this. # config.locales = {:en => 'English', :es => 'Español'} + # rubocop:enable Style/AsciiComments # Admin interface will respect the locale of the site being managed. However you can # force it to English by setting this to `:en` From 0f8747f03de8a1202fa993da4bea6665c90ea019 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 4 Jun 2018 17:13:00 +1200 Subject: [PATCH 149/219] Turn on the CMS specs again --- spec/features/cms_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/features/cms_spec.rb b/spec/features/cms_spec.rb index fa6ace64a..176c531a8 100644 --- a/spec/features/cms_spec.rb +++ b/spec/features/cms_spec.rb @@ -4,13 +4,13 @@ feature "cms admin" do let(:member) { create :member } let(:admin_member) { create :admin_member } - pending "can't view CMS admin if not signed in" do + scenario "can't view CMS admin if not signed in" do visit comfy_admin_cms_path expect(current_path).to eq root_path expect(page).to have_content "Please sign in as an admin user" end - pending "can't view CMS admin if not an admin member" do + scenario "can't view CMS admin if not an admin member" do # sign in as an ordinary member login_as member visit comfy_admin_cms_path @@ -18,7 +18,7 @@ feature "cms admin" do expect(page).to have_content "Please sign in as an admin user" end - pending "admin members can view CMS admin area" do + scenario "admin members can view CMS admin area" do login_as admin_member visit comfy_admin_cms_path expect(current_path).to match(/#{comfy_admin_cms_path}/) # match any CMS admin page From f52acd528253f3585286d1ea2f910cbd8b85d76e Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 13 Sep 2018 22:44:32 +1200 Subject: [PATCH 150/219] Adding puma to gem bundle --- Gemfile | 2 + Gemfile.lock | 125 ++++++++++++++++++++++++++------------------------- 2 files changed, 66 insertions(+), 61 deletions(-) diff --git a/Gemfile b/Gemfile index 942940db0..234304a0d 100644 --- a/Gemfile +++ b/Gemfile @@ -100,6 +100,8 @@ gem "paranoia", "~> 2.2" gem 'xmlrpc' # fixes rake error - can be removed if not needed later +gem 'puma' + group :production, :staging do gem 'bonsai-elasticsearch-rails' # Integration with Bonsa-Elasticsearch on heroku gem 'dalli' diff --git a/Gemfile.lock b/Gemfile.lock index 27dea9b08..f0592d19f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -72,15 +72,17 @@ GEM public_suffix (>= 2.0.2, < 4.0) arel (8.0.0) ast (2.4.0) - autoprefixer-rails (8.6.4) + autoprefixer-rails (9.1.4) execjs - bcrypt (3.1.11) - better_errors (2.4.0) + bcrypt (3.1.12) + better_errors (2.5.0) coderay (>= 1.0.0) erubi (>= 1.0.0) rack (>= 0.9.0) bluecloth (2.2.0) - bonsai-elasticsearch-rails (0.0.4) + bonsai-elasticsearch-rails (7.0.1) + elasticsearch-model (< 8) + elasticsearch-rails (< 8) bootstrap-datepicker-rails (1.8.0.1) railties (>= 3.0) bootstrap-kaminari-views (0.0.5) @@ -91,25 +93,25 @@ GEM sass (>= 3.3.4) bootstrap_form (2.7.0) builder (3.2.3) - bullet (5.7.5) + bullet (5.7.6) activesupport (>= 3.0.0) uniform_notifier (~> 1.11.0) byebug (10.0.2) cancancan (2.2.0) - capybara (2.18.0) + capybara (3.7.2) addressable mini_mime (>= 0.1.3) - nokogiri (>= 1.3.3) - rack (>= 1.0.0) - rack-test (>= 0.5.4) - xpath (>= 2.0, < 4.0) + nokogiri (~> 1.8) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + xpath (~> 3.1) capybara-email (3.0.1) capybara (>= 2.4, < 4.0) mail capybara-screenshot (1.0.21) capybara (>= 1.0, < 4) launchy - chartkick (2.3.5) + chartkick (3.0.1) childprocess (0.9.0) ffi (~> 1.0, >= 1.0.11) climate_control (0.2.0) @@ -127,7 +129,7 @@ GEM execjs coffee-script-source (1.12.2) concurrent-ruby (1.0.5) - connection_pool (2.2.1) + connection_pool (2.2.2) coveralls (0.8.19) json (>= 1.8, < 3) simplecov (~> 0.12.0) @@ -139,7 +141,7 @@ GEM activesupport (>= 3.0.0) dalli (2.7.8) database_cleaner (1.7.0) - devise (4.4.3) + devise (4.5.0) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0, < 6.0) @@ -147,9 +149,6 @@ GEM warden (~> 1.2.3) diff-lcs (1.3) docile (1.1.5) - easy_translate (0.5.1) - thread - thread_safe elasticsearch (6.1.0) elasticsearch-api (= 6.1.0) elasticsearch-transport (= 6.1.0) @@ -167,10 +166,10 @@ GEM erubis (2.7.0) excon (0.62.0) execjs (2.7.0) - factory_bot (4.10.0) + factory_bot (4.11.1) activesupport (>= 3.0.0) - factory_bot_rails (4.10.0) - factory_bot (~> 4.10.0) + factory_bot_rails (4.11.1) + factory_bot (~> 4.11.1) railties (>= 3.0.0) faker (1.9.1) i18n (>= 0.7) @@ -180,11 +179,11 @@ GEM figaro (1.1.1) thor (~> 0.14) flickraw (0.9.9) - font-awesome-sass (5.0.9) - sass (>= 3.2) + font-awesome-sass (5.3.1) + sassc (>= 1.11) friendly_id (5.2.4) activerecord (>= 4.0.0) - geocoder (1.4.9) + geocoder (1.5.0) gibbon (1.2.1) httparty multi_json (>= 1.9.0) @@ -208,7 +207,7 @@ GEM haml (>= 4.0.6, < 6.0) html2haml (>= 1.0.1) railties (>= 4.0.1) - haml_lint (0.27.0) + haml_lint (0.28.0) haml (>= 4.0, < 5.1) rainbow rake (>= 10, < 13) @@ -220,7 +219,7 @@ GEM excon moneta multi_json (>= 1.9.2) - highline (1.7.10) + highline (2.0.0) html2haml (2.2.0) erubis (~> 2.7.0) haml (>= 4.0, < 6) @@ -230,27 +229,27 @@ GEM multi_xml (>= 0.5.2) i18n (0.9.5) concurrent-ruby (~> 1.0) - i18n-tasks (0.9.21) + i18n-tasks (0.9.24) activesupport (>= 4.0.2) ast (>= 2.1.0) - easy_translate (>= 0.5.1) erubi - highline (>= 1.7.3) + highline (>= 2.0.0) i18n parser (>= 2.2.3.0) rainbow (>= 2.2.2, < 4.0) terminal-table (>= 1.5.1) + jaro_winkler (1.5.1) jquery-rails (4.3.3) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) jquery-ui-rails (6.0.1) railties (>= 3.2.16) - js-routes (1.4.3) + js-routes (1.4.4) railties (>= 3.2) sprockets-rails json (2.1.0) - jsonapi-resources (0.9.0) + jsonapi-resources (0.9.3) activerecord (>= 4.1) concurrent-ruby railties (>= 4.1) @@ -286,20 +285,20 @@ GEM mini_mime (>= 0.1.1) memcachier (0.0.2) method_source (0.9.0) - mime-types (3.1) + mime-types (3.2.2) mime-types-data (~> 3.2015) - mime-types-data (3.2016.0521) + mime-types-data (3.2018.0812) mimemagic (0.3.2) - mini_mime (1.0.0) + mini_mime (1.0.1) mini_portile2 (2.3.0) minitest (5.11.3) moneta (0.8.1) multi_json (1.11.3) multi_xml (0.6.0) multipart-post (2.0.0) - newrelic_rpm (5.0.0.342) - nio4r (2.3.0) - nokogiri (1.8.2) + newrelic_rpm (5.4.0.347) + nio4r (2.3.1) + nokogiri (1.8.4) mini_portile2 (~> 2.3.0) oauth (0.5.4) oauth2 (1.4.0) @@ -335,7 +334,7 @@ GEM parallel (1.12.1) paranoia (2.4.1) activerecord (>= 4.0, < 5.3) - parser (2.5.1.0) + parser (2.5.1.2) ast (~> 2.4.0) pg (0.21.0) platform-api (2.1.0) @@ -343,16 +342,17 @@ GEM moneta (~> 0.8.1) plupload-rails (1.2.1) rails (>= 3.1) - poltergeist (1.17.0) - capybara (~> 2.1) + poltergeist (1.18.1) + capybara (>= 2.1, < 4) cliver (~> 0.3.1) websocket-driver (>= 0.2.0) - powerpack (0.1.1) - public_suffix (3.0.2) + powerpack (0.1.2) + public_suffix (3.0.3) + puma (3.12.0) rack (2.0.5) - rack-protection (2.0.1) + rack-protection (2.0.3) rack - rack-test (1.0.0) + rack-test (1.1.0) rack (>= 1.0, < 3) rails (5.1.4) actioncable (= 5.1.4) @@ -366,9 +366,9 @@ GEM bundler (>= 1.3.0) railties (= 5.1.4) sprockets-rails (>= 2.0.0) - rails-assets-leaflet (1.3.1) - rails-assets-leaflet.markercluster (1.3.0) - rails-assets-leaflet (>= 1.0.3) + rails-assets-leaflet (1.3.4) + rails-assets-leaflet.markercluster (1.4.0) + rails-assets-leaflet (>= 1.3.1) rails-controller-testing (1.0.2) actionpack (~> 5.x, >= 5.0.1) actionview (~> 5.x, >= 5.0.1) @@ -398,7 +398,7 @@ GEM rb-fsevent (0.10.3) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) - redis (4.0.1) + redis (4.0.2) responders (2.4.0) actionpack (>= 4.2.0, < 5.3) railties (>= 4.2.0, < 5.3) @@ -418,25 +418,26 @@ GEM actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec-core (~> 3.7.0) - rspec-expectations (~> 3.7.0) - rspec-mocks (~> 3.7.0) - rspec-support (~> 3.7.0) - rspec-support (3.7.1) - rubocop (0.55.0) + rspec-core (~> 3.8.0) + rspec-expectations (~> 3.8.0) + rspec-mocks (~> 3.8.0) + rspec-support (~> 3.8.0) + rspec-support (3.8.0) + rubocop (0.59.0) + jaro_winkler (~> 1.5.1) parallel (~> 1.10) - parser (>= 2.5) + parser (>= 2.5, != 2.5.1.1) powerpack (~> 0.1) rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) - ruby-progressbar (1.9.0) + ruby-progressbar (1.10.0) ruby-units (2.3.0) ruby_dep (1.5.0) ruby_parser (3.11.0) sexp_processor (~> 4.9) rubyzip (1.2.2) - sass (3.5.6) + sass (3.5.7) sass-listen (~> 4.0.0) sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) @@ -447,13 +448,15 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) + sassc (1.12.1) + ffi (~> 1.9.6) + sass (>= 3.3.0) selenium-webdriver (3.14.0) childprocess (~> 0.5) rubyzip (~> 1.2) sexp_processor (4.11.0) - sidekiq (5.1.3) - concurrent-ruby (~> 1.0) - connection_pool (~> 2.2, >= 2.2.0) + sidekiq (5.2.1) + connection_pool (~> 2.2, >= 2.2.2) rack-protection (>= 1.5.0) redis (>= 3.3.5, < 5) simplecov (0.12.0) @@ -479,7 +482,6 @@ GEM terrapin (0.6.0) climate_control (>= 0.0.3, < 1.0) thor (0.19.4) - thread (0.2.2) thread_safe (0.3.6) tilt (2.0.8) timecop (0.9.1) @@ -487,9 +489,9 @@ GEM trollop (1.16.2) tzinfo (1.2.5) thread_safe (~> 0.1) - uglifier (4.1.15) + uglifier (4.1.19) execjs (>= 0.3.0, < 3) - unicode-display_width (1.3.2) + unicode-display_width (1.4.0) unicorn (5.4.1) kgio (~> 2.6) raindrops (~> 0.7) @@ -573,6 +575,7 @@ DEPENDENCIES pg (< 1.0.0) platform-api poltergeist + puma rack-protection (>= 2.0.1) rails (= 5.1.4) rails-assets-leaflet.markercluster! From aad7128a316c610cac2ee2d40ade77702b51ddd1 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 13 Sep 2018 22:44:46 +1200 Subject: [PATCH 151/219] Fixed user name generation in member factorybot --- spec/factories/member.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/factories/member.rb b/spec/factories/member.rb index d293fc90b..a544e465a 100644 --- a/spec/factories/member.rb +++ b/spec/factories/member.rb @@ -1,6 +1,6 @@ FactoryBot.define do factory :member, aliases: %i(author owner sender recipient creator) do - login_name { generate(:login_name) } + login_name { (0...8).map { (65 + rand(26)).chr }.join } password { 'password1' } email { Faker::Internet.unique.email } tos_agreement { true } From 99a9c0817dbd06aadc6f88fecfbf390322b38720 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 13 Sep 2018 22:51:22 +1200 Subject: [PATCH 152/219] Lint fix --- lib/tasks/growstuff.rake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/tasks/growstuff.rake b/lib/tasks/growstuff.rake index 9fe5202d3..4a6234c4b 100644 --- a/lib/tasks/growstuff.rake +++ b/lib/tasks/growstuff.rake @@ -116,6 +116,7 @@ namespace :growstuff do task set_default_crop_creator: :environment do cropbot = Member.find_by(login_name: "cropbot") raise "cropbot not found: create cropbot member on site or run rake db:seed" unless cropbot + Crop.find_each do |crop| unless crop.creator crop.creator = cropbot @@ -150,6 +151,7 @@ namespace :growstuff do Member.located.find_each do |m| m.gardens.each do |g| next if g.location.present? + g.location = m.location g.latitude = m.latitude g.longitude = m.longitude @@ -218,6 +220,7 @@ namespace :growstuff do CSV.foreach(file) do |row| _crop_id, crop_name, alternate_names = row next if alternate_names.blank? + crop = Crop.find_by(name: crop_name) if crop alternate_names.split(/,\s*/).each do |an| From c16ee81c88561d06cdf3eba86de26844bd777b17 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 13 Sep 2018 22:51:42 +1200 Subject: [PATCH 153/219] Add rubocop-rspec --- Gemfile | 1 + Gemfile.lock | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index 234304a0d..80fc29b82 100644 --- a/Gemfile +++ b/Gemfile @@ -134,6 +134,7 @@ group :development, :test do gem 'poltergeist' # for headless JS testing gem 'rspec-activemodel-mocks' gem 'rspec-rails' # unit testing framework + gem 'rubocop-rspec' gem 'rubocop', '>= 0.54.0' gem 'selenium-webdriver' gem 'webrat' # provides HTML matchers for view tests diff --git a/Gemfile.lock b/Gemfile.lock index f0592d19f..0a7379bff 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -431,6 +431,8 @@ GEM rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) + rubocop-rspec (1.29.1) + rubocop (>= 0.58.0) ruby-progressbar (1.10.0) ruby-units (2.3.0) ruby_dep (1.5.0) @@ -586,6 +588,7 @@ DEPENDENCIES rspec-activemodel-mocks rspec-rails rubocop (>= 0.54.0) + rubocop-rspec ruby-units sass-rails selenium-webdriver From ee49e8c5db9dfc2720031e97693dc3230ad3dfbe Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 13 Sep 2018 23:09:13 +1200 Subject: [PATCH 154/219] Fixing unneeded condition --- spec/features/rss/plantings_spec.rb | 2 +- spec/features/rss/posts_spec.rb | 2 +- spec/features/rss/seeds_spec.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/features/rss/plantings_spec.rb b/spec/features/rss/plantings_spec.rb index 59833eff1..c3f68fea6 100644 --- a/spec/features/rss/plantings_spec.rb +++ b/spec/features/rss/plantings_spec.rb @@ -9,6 +9,6 @@ feature 'Plantings RSS feed' do scenario 'The index title is what we expect' do visit plantings_path(format: 'rss') expect(page).to have_content "Recent plantings from "\ - "#{@owner ? @owner : 'all members'} (#{ENV['GROWSTUFF_SITE_NAME']})" + "#{@owner || 'all members'} (#{ENV['GROWSTUFF_SITE_NAME']})" end end diff --git a/spec/features/rss/posts_spec.rb b/spec/features/rss/posts_spec.rb index 445a50cca..668276fbb 100644 --- a/spec/features/rss/posts_spec.rb +++ b/spec/features/rss/posts_spec.rb @@ -9,6 +9,6 @@ feature 'Posts RSS feed' do scenario 'The index title is what we expect' do visit posts_path(format: 'rss') expect(page).to have_content "Recent posts from "\ - "#{@author ? @author : 'all members'} (#{ENV['GROWSTUFF_SITE_NAME']})" + "#{@author || 'all members'} (#{ENV['GROWSTUFF_SITE_NAME']})" end end diff --git a/spec/features/rss/seeds_spec.rb b/spec/features/rss/seeds_spec.rb index 888cc42ec..abb66e7e3 100644 --- a/spec/features/rss/seeds_spec.rb +++ b/spec/features/rss/seeds_spec.rb @@ -9,6 +9,6 @@ feature 'Seeds RSS feed' do scenario 'The index title is what we expect' do visit seeds_path(format: 'rss') expect(page).to have_content "Recent seeds from "\ - "#{@owner ? @owner : 'all members'} (#{ENV['GROWSTUFF_SITE_NAME']})" + "#{@owner || 'all members'} (#{ENV['GROWSTUFF_SITE_NAME']})" end end From 3ad048b7e85978db69fecaa0478b3bbb045ae73a Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 13 Sep 2018 23:10:43 +1200 Subject: [PATCH 155/219] Fixed linter issue, empty line after guard clause --- app/controllers/crops_controller.rb | 2 ++ app/controllers/photo_associations_controller.rb | 1 + app/controllers/photos_controller.rb | 3 +++ app/helpers/application_helper.rb | 1 + app/helpers/harvests_helper.rb | 1 + app/helpers/plantings_helper.rb | 2 ++ app/models/concerns/predict_harvest.rb | 2 ++ app/models/concerns/predict_planting.rb | 1 + app/models/crop.rb | 5 +++++ app/models/csv_importer.rb | 1 + app/models/harvest.rb | 8 ++++++++ app/models/member.rb | 4 ++++ app/models/planting.rb | 1 + app/models/post.rb | 1 + config/initializers/comfortable_mexican_sofa.rb | 1 + lib/geocodable.rb | 1 + spec/lib/haml/filters/growstuff_markdown_spec.rb | 2 ++ spec/support/elasticsearch_helpers.rb | 1 + 18 files changed, 38 insertions(+) diff --git a/app/controllers/crops_controller.rb b/app/controllers/crops_controller.rb index 361e28031..c5b268f8b 100644 --- a/app/controllers/crops_controller.rb +++ b/app/controllers/crops_controller.rb @@ -128,6 +128,7 @@ class CropsController < ApplicationController def notify_wranglers return if current_member.role? :crop_wrangler + Role.crop_wranglers.each do |w| Notifier.new_crop_request(w, @crop).deliver_now! end @@ -135,6 +136,7 @@ class CropsController < ApplicationController def recreate_names(param_name, name_type) return if params[param_name].blank? + destroy_names(name_type) params[param_name].each do |_i, value| create_name!(name_type, value) unless value.empty? diff --git a/app/controllers/photo_associations_controller.rb b/app/controllers/photo_associations_controller.rb index c63cca398..743966d5d 100644 --- a/app/controllers/photo_associations_controller.rb +++ b/app/controllers/photo_associations_controller.rb @@ -4,6 +4,7 @@ class PhotoAssociationsController < ApplicationController def destroy raise "Photos not supported" unless Photo::PHOTO_CAPABLE.include? item_class + @photo = Photo.find_by!(id: params[:photo_id], owner: current_member) @item = Photographing.item(item_id, item_class) @item.photos.delete(@photo) diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb index c9dd8886f..331a4e2c4 100644 --- a/app/controllers/photos_controller.rb +++ b/app/controllers/photos_controller.rb @@ -36,6 +36,7 @@ class PhotosController < ApplicationController @photo = find_or_create_photo_from_flickr_photo @item = item_to_link_to raise "Could not find this #{type} owned by you" unless @item + @item.photos << @photo unless @item.photos.include? @photo @photo.save! if @photo.present? end @@ -77,8 +78,10 @@ class PhotosController < ApplicationController def item_to_link_to raise "No item id provided" if item_id.nil? raise "No item type provided" if item_type.nil? + item_class = item_type.capitalize raise "Photos not supported" unless Photo::PHOTO_CAPABLE.include? item_class + item_class.constantize.find(params[:id]) end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index ca4bc9429..97ff16b76 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -39,6 +39,7 @@ module ApplicationHelper # def avatar_uri(member, size = 150) return unless member + if member.preferred_avatar_uri.present? # Some avatars support different sizes # http://graph.facebook.com/12345678/picture?width=150&height=150 diff --git a/app/helpers/harvests_helper.rb b/app/helpers/harvests_helper.rb index 10ed30888..ff1838ce4 100644 --- a/app/helpers/harvests_helper.rb +++ b/app/helpers/harvests_helper.rb @@ -24,6 +24,7 @@ module HarvestsHelper def display_weight(harvest) return if harvest.weight_quantity.blank? || harvest.weight_quantity <= 0 + "#{number_to_human(harvest.weight_quantity, strip_insignificant_zeros: true)} #{harvest.weight_unit}" end diff --git a/app/helpers/plantings_helper.rb b/app/helpers/plantings_helper.rb index 5f448975e..2ebe7569f 100644 --- a/app/helpers/plantings_helper.rb +++ b/app/helpers/plantings_helper.rb @@ -35,11 +35,13 @@ module PlantingsHelper def days_from_now_to_finished(planting) return unless planting.finish_is_predicatable? + (planting.finish_predicted_at - Time.zone.today).to_i end def days_from_now_to_first_harvest(planting) return unless planting.planted_at.present? && planting.first_harvest_predicted_at.present? + (planting.first_harvest_predicted_at - Time.zone.today).to_i end diff --git a/app/models/concerns/predict_harvest.rb b/app/models/concerns/predict_harvest.rb index d5422ee35..bc377f369 100644 --- a/app/models/concerns/predict_harvest.rb +++ b/app/models/concerns/predict_harvest.rb @@ -13,11 +13,13 @@ module PredictHarvest def first_harvest_predicted_at return unless crop.median_days_to_first_harvest.present? && planted_at.present? + planted_at + crop.median_days_to_first_harvest.days end def last_harvest_predicted_at return unless crop.median_days_to_last_harvest.present? && planted_at.present? + planted_at + crop.median_days_to_last_harvest.days end diff --git a/app/models/concerns/predict_planting.rb b/app/models/concerns/predict_planting.rb index ff66168d8..c7ca81d01 100644 --- a/app/models/concerns/predict_planting.rb +++ b/app/models/concerns/predict_planting.rb @@ -33,6 +33,7 @@ module PredictPlanting def actual_lifespan return unless planted_at.present? && finished_at.present? + (finished_at - planted_at).to_i end diff --git a/app/models/crop.rb b/app/models/crop.rb index 45df93ad2..3da542aee 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -156,6 +156,7 @@ class Crop < ApplicationRecord min_photos = 3 # needs this many photos to be interesting return false unless photos.size >= min_photos return false unless plantings_count >= min_plantings + true end @@ -181,6 +182,7 @@ class Crop < ApplicationRecord def rejection_explanation return rejection_notes if reason_for_rejection == "other" + reason_for_rejection end @@ -222,17 +224,20 @@ class Crop < ApplicationRecord def approval_status_cannot_be_changed_again previous = previous_changes.include?(:approval_status) ? previous_changes.approval_status : {} return unless previous.include?(:rejected) || previous.include?(:approved) + errors.add(:approval_status, "has already been set to #{approval_status}") end def must_be_rejected_if_rejected_reasons_present return if rejected? return unless reason_for_rejection.present? || rejection_notes.present? + errors.add(:approval_status, "must be rejected if a reason for rejection is present") end def must_have_meaningful_reason_for_rejection return unless reason_for_rejection == "other" && rejection_notes.blank? + errors.add(:rejection_notes, "must be added if the reason for rejection is \"other\"") end diff --git a/app/models/csv_importer.rb b/app/models/csv_importer.rb index 4205a6c13..d3b992aba 100644 --- a/app/models/csv_importer.rb +++ b/app/models/csv_importer.rb @@ -54,6 +54,7 @@ class CsvImporter def add_alternate_names(alternate_names) # i.e. we actually passed something in, which isn't a given return if alternate_names.blank? + alternate_names.split(/,\s*/).each do |name| altname = AlternateName.find_by(name: name, crop: @crop) altname ||= AlternateName.create! name: name, crop: @crop, creator: cropbot diff --git a/app/models/harvest.rb b/app/models/harvest.rb index c9829fed7..987c83d0b 100644 --- a/app/models/harvest.rb +++ b/app/models/harvest.rb @@ -70,6 +70,7 @@ class Harvest < ApplicationRecord def time_from_planting_to_harvest return if planting.blank? + harvested_at - planting.planted_at end @@ -77,6 +78,7 @@ class Harvest < ApplicationRecord # to make data manipulation easier def set_si_weight return if weight_unit.nil? + weight_string = "#{weight_quantity} #{weight_unit}" self.si_weight = Unit.new(weight_string).convert_to("kg").to_s("%0.3f").delete(" kg").to_f end @@ -101,11 +103,13 @@ class Harvest < ApplicationRecord def quantity_to_human return number_to_human(quantity.to_s, strip_insignificant_zeros: true) if quantity + "" end def unit_to_human return "" unless quantity + if unit == 'individual' 'individual' elsif quantity == 1 @@ -117,6 +121,7 @@ class Harvest < ApplicationRecord def weight_to_human return "" unless weight_quantity + "weighing #{number_to_human(weight_quantity, strip_insignificant_zeros: true)} #{weight_unit}" end @@ -138,17 +143,20 @@ class Harvest < ApplicationRecord def crop_must_match_planting return if planting.blank? # only check if we are linked to a planting + errors.add(:planting, "must be the same crop") unless crop == planting.crop end def owner_must_match_planting return if planting.blank? # only check if we are linked to a planting + errors.add(:owner, "of harvest must be the same as planting") unless owner == planting.owner end def harvest_must_be_after_planting # only check if we are linked to a planting return unless harvested_at.present? && planting.present? && planting.planted_at.present? + errors.add(:planting, "cannot be harvested before planting") unless harvested_at > planting.planted_at end end diff --git a/app/models/member.rb b/app/models/member.rb index 655f3c5aa..ec3414c77 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -85,6 +85,7 @@ class Member < ApplicationRecord conditions = warden_conditions.dup login = conditions.delete(:login) return where(conditions).login_name_or_email(login).first if login + find_by(conditions) end @@ -133,6 +134,7 @@ class Member < ApplicationRecord ) end return [result.photo, result.total] if result + [[], 0] end @@ -182,6 +184,7 @@ class Member < ApplicationRecord def newsletter_subscribe(gibbon = Gibbon::API.new, testing = false) return true if Rails.env.test? && !testing + gibbon.lists.subscribe( id: Rails.application.config.newsletter_list_id, email: { email: email }, @@ -192,6 +195,7 @@ class Member < ApplicationRecord def newsletter_unsubscribe(gibbon = Gibbon::API.new, testing = false) return true if Rails.env.test? && !testing + gibbon.lists.unsubscribe(id: Rails.application.config.newsletter_list_id, email: { email: email }) end diff --git a/app/models/planting.rb b/app/models/planting.rb index f6966d94f..61b75e326 100644 --- a/app/models/planting.rb +++ b/app/models/planting.rb @@ -101,6 +101,7 @@ class Planting < ApplicationRecord # check that any finished_at date occurs after planted_at def finished_must_be_after_planted return unless planted_at && finished_at # only check if we have both + errors.add(:finished_at, "must be after the planting date") unless planted_at < finished_at end diff --git a/app/models/post.rb b/app/models/post.rb index 2dc254741..bde34cfe3 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -77,6 +77,7 @@ class Post < ApplicationRecord # don't send notifications to yourself recipients.map(&:id).each do |recipient_id| next unless recipient_id != sender + Notification.create( recipient_id: recipient_id, sender_id: sender, diff --git a/config/initializers/comfortable_mexican_sofa.rb b/config/initializers/comfortable_mexican_sofa.rb index 314bc21fe..43630ef0a 100644 --- a/config/initializers/comfortable_mexican_sofa.rb +++ b/config/initializers/comfortable_mexican_sofa.rb @@ -96,6 +96,7 @@ end module CmsDeviseAuth def authenticate return if current_member&.role?(:admin) + redirect_to root_path, alert: 'Permission denied. Please sign in as an admin user to use the CMS admin area.' end end diff --git a/lib/geocodable.rb b/lib/geocodable.rb index 7e00c7566..f1bb243f6 100644 --- a/lib/geocodable.rb +++ b/lib/geocodable.rb @@ -7,6 +7,7 @@ module Geocodable def empty_unwanted_geocodes return if location.present? + self.latitude = nil self.longitude = nil end diff --git a/spec/lib/haml/filters/growstuff_markdown_spec.rb b/spec/lib/haml/filters/growstuff_markdown_spec.rb index 58626d036..e4dea52d9 100644 --- a/spec/lib/haml/filters/growstuff_markdown_spec.rb +++ b/spec/lib/haml/filters/growstuff_markdown_spec.rb @@ -9,6 +9,7 @@ end def output_link(crop, name = nil) url = Rails.application.routes.url_helpers.crop_url(crop, host: Rails.application.config.host) return "#{name}" if name + "#{crop.name}" end @@ -19,6 +20,7 @@ end def output_member_link(member, name = nil) url = Rails.application.routes.url_helpers.member_url(member, only_path: true) return "#{name}" if name + "#{member.login_name}" end diff --git a/spec/support/elasticsearch_helpers.rb b/spec/support/elasticsearch_helpers.rb index aaa4fd689..7cd3fe5f2 100644 --- a/spec/support/elasticsearch_helpers.rb +++ b/spec/support/elasticsearch_helpers.rb @@ -1,6 +1,7 @@ module ElasticsearchHelpers def sync_elasticsearch(crops) return unless ENV['GROWSTUFF_ELASTICSEARCH'] == "true" + crops.each { |crop| crop.__elasticsearch__.index_document } Crop.__elasticsearch__.refresh_index! end From 7c7d261e7e445ce0574fe113fba9caccb68f9250 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 13 Sep 2018 23:13:03 +1200 Subject: [PATCH 156/219] Fixed last static factorybot attribtue --- spec/factories/notifications.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/factories/notifications.rb b/spec/factories/notifications.rb index 32c4aad94..5b734ed28 100644 --- a/spec/factories/notifications.rb +++ b/spec/factories/notifications.rb @@ -5,7 +5,7 @@ FactoryBot.define do sender { FactoryBot.create :member } recipient { FactoryBot.create :member } - subject "MyString" + subject { "MyString" } body { "MyText" } read { false } post From e2874404e471fd80ace4883b50bea1618c774aa5 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 13 Sep 2018 23:15:01 +1200 Subject: [PATCH 157/219] Layout linter fix --- spec/factories/notifications.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/factories/notifications.rb b/spec/factories/notifications.rb index 5b734ed28..b0b3dce97 100644 --- a/spec/factories/notifications.rb +++ b/spec/factories/notifications.rb @@ -2,7 +2,6 @@ FactoryBot.define do factory :notification, aliases: [:message] do - sender { FactoryBot.create :member } recipient { FactoryBot.create :member } subject { "MyString" } From b3ecdb0ae533e025d6542e47790b8d0daa0fcffd Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Thu, 13 Sep 2018 23:16:16 +1200 Subject: [PATCH 158/219] Removed include in rubocop, was making rubocop not check files --- .rubocop.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 9a8a0a405..316ffcde7 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,13 +1,9 @@ inherit_from: .rubocop_todo.yml AllCops: - Include: - - 'Rakefile' - - 'config.ru' - - 'lib/**/*.rake' Exclude: - 'db/schema.rb' - 'vendor/**/*' - TargetRailsVersion: 4.0 + TargetRailsVersion: 5.0 Rails: Enabled: true @@ -21,7 +17,6 @@ Naming/FileName: Style/StringLiterals: Enabled: false -# Stop hound and codeclimate fighting Style/PercentLiteralDelimiters: PreferredDelimiters: default: () @@ -36,8 +31,6 @@ Layout/MultilineMethodCallIndentation: Layout/AlignParameters: EnforcedStyle: with_fixed_indentation -Metrics/LineLength: - Max: 120 Style/Documentation: Enabled: false @@ -58,6 +51,9 @@ Metrics/BlockLength: - '**/*.rake' - 'config/**/*.rb' +Metrics/LineLength: + Max: 120 + # Remove the following once the code style matches Metrics/MethodLength: Max: 34 From 5031b37232386d1c8d84592e5366c7af415871fd Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 29 Sep 2018 15:51:35 +1200 Subject: [PATCH 159/219] Adding \n into specs, because our output has it now --- .../plantings/planting_a_crop_spec.rb | 20 +++-- spec/features/seeds/adding_seeds_spec.rb | 85 ++++++++++--------- spec/features/shared_examples/append_date.rb | 2 +- 3 files changed, 56 insertions(+), 51 deletions(-) diff --git a/spec/features/plantings/planting_a_crop_spec.rb b/spec/features/plantings/planting_a_crop_spec.rb index 0e26d7078..bde169dd0 100644 --- a/spec/features/plantings/planting_a_crop_spec.rb +++ b/spec/features/plantings/planting_a_crop_spec.rb @@ -211,7 +211,7 @@ feature "Planting a crop", :js, :elasticsearch do click_button "Save" end expect(page).to have_content "planting was successfully created" - expect(page).to have_content "Finished: August 30, 2014" + expect(page).to have_content "Finished:\nAugust 30, 2014" # shouldn't be on the page visit plantings_path @@ -223,15 +223,17 @@ feature "Planting a crop", :js, :elasticsearch do end scenario "Marking a planting as finished without a date" do - fill_autocomplete "crop", with: "mai" - select_from_autocomplete "maize" - within "form#new_planting" do - check "Mark as finished" - click_button "Save" + before do + fill_autocomplete "crop", with: "mai" + select_from_autocomplete "maize" + within "form#new_planting" do + check "Mark as finished" + click_button "Save" + end end - expect(page).to have_content "planting was successfully created" - expect(page).to have_content "Finished: Yes (no date specified)" - expect(page).to have_content "100%" + it { expect(page).to have_content "planting was successfully created" } + it { expect(page).to have_content "Finished:\nYes (no date specified)" } + it { expect(page).to have_content "100%" } end describe "Planting sunniness" do diff --git a/spec/features/seeds/adding_seeds_spec.rb b/spec/features/seeds/adding_seeds_spec.rb index 4c183ae24..8a875cc85 100644 --- a/spec/features/seeds/adding_seeds_spec.rb +++ b/spec/features/seeds/adding_seeds_spec.rb @@ -17,53 +17,56 @@ feature "Seeds", :js, :elasticsearch do expect(page).to have_content "* denotes a required field" end - it "displays required and optional fields properly" do - expect(page).to have_selector ".form-group.required", text: "Crop:" - expect(page).to have_optional 'input#seed_quantity' - expect(page).to have_optional 'input#seed_plant_before' - expect(page).to have_optional 'input#seed_days_until_maturity_min' - expect(page).to have_optional 'input#seed_days_until_maturity_max' - expect(page).to have_selector '.form-group.required', text: 'Organic?' - expect(page).to have_selector '.form-group.required', text: 'GMO?' - expect(page).to have_selector '.form-group.required', text: 'Heirloom?' - expect(page).to have_optional 'textarea#seed_description' - expect(page).to have_selector '.form-group.required', text: 'Will trade:' + describe "displays required and optional fields properly" do + it { expect(page).to have_selector ".form-group.required", text: "Crop:" } + it { expect(page).to have_optional 'input#seed_quantity' } + it { expect(page).to have_optional 'input#seed_plant_before' } + it { expect(page).to have_optional 'input#seed_days_until_maturity_min' } + it { expect(page).to have_optional 'input#seed_days_until_maturity_max' } + it { expect(page).to have_selector '.form-group.required', text: 'Organic?' } + it { expect(page).to have_selector '.form-group.required', text: 'GMO?' } + it { expect(page).to have_selector '.form-group.required', text: 'Heirloom?' } + it { expect(page).to have_optional 'textarea#seed_description' } + it { expect(page).to have_selector '.form-group.required', text: 'Will trade:' } end - scenario "Adding a new seed", js: true do - fill_autocomplete "crop", with: "mai" - select_from_autocomplete "maize" - within "form#new_seed" do - fill_in "Quantity:", with: 42 - fill_in "Plant before:", with: "2014-06-15" - fill_in "Days until maturity:", with: 999 - fill_in "to", with: 1999 - select "certified organic", from: "Organic?" - select "non-certified GMO-free", from: "GMO?" - select "heirloom", from: "Heirloom?" - fill_in "Description", with: "It's killer." - select "internationally", from: "Will trade:" - click_button "Save" + describe "Adding a new seed", js: true do + before do + fill_autocomplete "crop", with: "mai" + select_from_autocomplete "maize" + within "form#new_seed" do + fill_in "Quantity:", with: 42 + fill_in "Plant before:", with: "2014-06-15" + fill_in "Days until maturity:", with: 999 + fill_in "to", with: 1999 + select "certified organic", from: "Organic?" + select "non-certified GMO-free", from: "GMO?" + select "heirloom", from: "Heirloom?" + fill_in "Description", with: "It's killer." + select "internationally", from: "Will trade:" + click_button "Save" + end end - - expect(page).to have_content "Successfully added maize seed to your stash" - expect(page).to have_content "Quantity: 42" - expect(page).to have_content "Days until maturity: 999–1999" - expect(page).to have_content "certified organic" - expect(page).to have_content "non-certified GMO-free" - expect(page).to have_content "Heirloom? heirloom" - expect(page).to have_content "It's killer." + it { expect(page).to have_content "Successfully added maize seed to your stash" } + it { expect(page).to have_content "Quantity:\n42" } + it { expect(page).to have_content "Days until maturity:\n999–1999" } + it { expect(page).to have_content "certified organic" } + it { expect(page).to have_content "non-certified GMO-free" } + it { expect(page).to have_content "Heirloom?\nheirloom" } + it { expect(page).to have_content "It's killer." } end - scenario "Adding a seed from crop page" do - visit crop_path(maize) - click_link "Add seeds to stash" - within "form#new_seed" do - expect(page).to have_selector "input[value='maize']" - click_button "Save" + describe "Adding a seed from crop page" do + before do + visit crop_path(maize) + click_link "Add seeds to stash" + within "form#new_seed" do + expect(page).to have_selector "input[value='maize']" + click_button "Save" + end end - expect(page).to have_content "Successfully added maize seed to your stash" - expect(page).to have_content "maize" + it { expect(page).to have_content "Successfully added maize seed to your stash" } + it { expect(page).to have_content "maize" } end end diff --git a/spec/features/shared_examples/append_date.rb b/spec/features/shared_examples/append_date.rb index 03e29c5f4..64de66e13 100644 --- a/spec/features/shared_examples/append_date.rb +++ b/spec/features/shared_examples/append_date.rb @@ -10,7 +10,7 @@ shared_examples "append date" do expect(page).to have_content this_month.to_s find(".datepicker-days td.day", text: "21").click end - expect(page).to have_content "Finished: #{this_month} 21, #{this_year}" + expect(page).to have_content "Finished:\n#{this_month} 21, #{this_year}" end scenario "Confirming without selecting date" do From d1d1a61be36e9d89ac2a3dfb9b01f6cba65aad73 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 29 Sep 2018 15:52:32 +1200 Subject: [PATCH 160/219] Added inverse to model --- app/models/comment.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/comment.rb b/app/models/comment.rb index 3eda14aa4..7c98a919a 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,5 +1,5 @@ class Comment < ApplicationRecord - belongs_to :author, -> { with_deleted }, class_name: 'Member' + belongs_to :author, -> { with_deleted }, class_name: 'Member', inverse_of: :comment belongs_to :post scope :post_order, -> { reorder("created_at ASC") } # for display on post page From 9694696570fc8f1971ff8d852b48343ffad70af0 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 29 Sep 2018 15:52:42 +1200 Subject: [PATCH 161/219] Code style fix ups --- spec/controllers/plantings_controller_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/controllers/plantings_controller_spec.rb b/spec/controllers/plantings_controller_spec.rb index ebe0e00eb..7f100d76b 100644 --- a/spec/controllers/plantings_controller_spec.rb +++ b/spec/controllers/plantings_controller_spec.rb @@ -19,7 +19,7 @@ describe PlantingsController do let!(:planting2) { FactoryBot.create :planting, crop: maize, owner: member2, created_at: 5.days.ago } describe "assigns all plantings as @plantings" do - before { get :index, {} } + before { get :index } it { expect(assigns(:plantings)).to match [planting1, planting2] } end @@ -82,7 +82,7 @@ describe PlantingsController do end describe "sets the date of the planting to today" do - before { get :new, {} } + before { get :new } it { expect(assigns(:planting).planted_at).to eq Time.zone.today } end From cd1bf8c2682ea92f0783f933aa6bc798d43223f2 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 29 Sep 2018 15:53:06 +1200 Subject: [PATCH 162/219] Code style fix up --- spec/factories/member.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/factories/member.rb b/spec/factories/member.rb index a544e465a..fcd72c05e 100644 --- a/spec/factories/member.rb +++ b/spec/factories/member.rb @@ -1,6 +1,6 @@ FactoryBot.define do factory :member, aliases: %i(author owner sender recipient creator) do - login_name { (0...8).map { (65 + rand(26)).chr }.join } + login_name { (0...8).map { rand(65..90).chr }.join } password { 'password1' } email { Faker::Internet.unique.email } tos_agreement { true } From 1bb70d4bcd19e6b2fc868e0b56adf911e58fa704 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 29 Sep 2018 15:55:07 +1200 Subject: [PATCH 163/219] Ordering gems --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 80fc29b82..31250e27a 100644 --- a/Gemfile +++ b/Gemfile @@ -134,8 +134,8 @@ group :development, :test do gem 'poltergeist' # for headless JS testing gem 'rspec-activemodel-mocks' gem 'rspec-rails' # unit testing framework - gem 'rubocop-rspec' gem 'rubocop', '>= 0.54.0' + gem 'rubocop-rspec' gem 'selenium-webdriver' gem 'webrat' # provides HTML matchers for view tests end From fb2f5f464a96c42809cf68fb3283122e0f0bdc6e Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 8 Oct 2018 09:47:52 +1300 Subject: [PATCH 164/219] Change scenario to description --- spec/features/plantings/planting_a_crop_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/plantings/planting_a_crop_spec.rb b/spec/features/plantings/planting_a_crop_spec.rb index bde169dd0..3da396ee0 100644 --- a/spec/features/plantings/planting_a_crop_spec.rb +++ b/spec/features/plantings/planting_a_crop_spec.rb @@ -222,7 +222,7 @@ feature "Planting a crop", :js, :elasticsearch do expect(page).to have_content "August 30, 2014" end - scenario "Marking a planting as finished without a date" do + describe "Marking a planting as finished without a date" do before do fill_autocomplete "crop", with: "mai" select_from_autocomplete "maize" From 05da2de7e47fbdc85f41490786de492cb7a1a445 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 8 Oct 2018 09:48:49 +1300 Subject: [PATCH 165/219] Fix up text in checking appended date --- spec/features/shared_examples/append_date.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/shared_examples/append_date.rb b/spec/features/shared_examples/append_date.rb index 64de66e13..74c7f8497 100644 --- a/spec/features/shared_examples/append_date.rb +++ b/spec/features/shared_examples/append_date.rb @@ -16,6 +16,6 @@ shared_examples "append date" do scenario "Confirming without selecting date" do click_link link_text click_link "Confirm without date" - expect(page).to have_content("Finished: Yes (no date specified) ") + expect(page).to have_content("Finished:\nYes (no date specified) ") end end From 1e7e275684549846e11427427a8af93973e660d9 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 8 Oct 2018 13:43:09 +1300 Subject: [PATCH 166/219] Adding inverse relationships (except for photos) --- app/models/comment.rb | 2 +- app/models/crop.rb | 4 ++-- app/models/member.rb | 26 ++++++++++++++------------ app/models/seed.rb | 3 ++- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/app/models/comment.rb b/app/models/comment.rb index 7c98a919a..3c9f3ce42 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,5 +1,5 @@ class Comment < ApplicationRecord - belongs_to :author, -> { with_deleted }, class_name: 'Member', inverse_of: :comment + belongs_to :author, -> { with_deleted }, class_name: 'Member', inverse_of: :comments belongs_to :post scope :post_order, -> { reorder("created_at ASC") } # for display on post page diff --git a/app/models/crop.rb b/app/models/crop.rb index 3da542aee..ba4787b97 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -19,8 +19,8 @@ class Crop < ApplicationRecord belongs_to :creator, class_name: 'Member', optional: true belongs_to :requester, class_name: 'Member', optional: true belongs_to :parent, class_name: 'Crop', optional: true - has_many :varieties, class_name: 'Crop', foreign_key: 'parent_id', dependent: :nullify - has_and_belongs_to_many :posts # rubocop:disable Rails/HasAndBelongsToMany + has_many :varieties, class_name: 'Crop', foreign_key: 'parent_id', dependent: :nullify, inverse_of: :parent + has_and_belongs_to_many :posts ## ## Scopes diff --git a/app/models/member.rb b/app/models/member.rb index ec3414c77..69f5e61b6 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -8,21 +8,23 @@ class Member < ApplicationRecord # # Relationships - has_many :posts, foreign_key: 'author_id', dependent: :destroy - has_many :comments, foreign_key: 'author_id', dependent: :destroy - has_many :forums, foreign_key: 'owner_id', dependent: :nullify - has_many :gardens, foreign_key: 'owner_id', dependent: :destroy - has_many :plantings, foreign_key: 'owner_id', dependent: :destroy - has_many :seeds, foreign_key: 'owner_id', dependent: :destroy - has_many :harvests, foreign_key: 'owner_id', dependent: :destroy + has_many :posts, foreign_key: 'author_id', dependent: :destroy, inverse_of: :author + has_many :comments, foreign_key: 'author_id', dependent: :destroy, inverse_of: :author + has_many :forums, foreign_key: 'owner_id', dependent: :nullify, inverse_of: :owner + has_many :gardens, foreign_key: 'owner_id', dependent: :destroy, inverse_of: :owner + has_many :plantings, foreign_key: 'owner_id', dependent: :destroy, inverse_of: :owner + has_many :seeds, foreign_key: 'owner_id', dependent: :destroy, inverse_of: :owner + has_many :harvests, foreign_key: 'owner_id', dependent: :destroy, inverse_of: :owner has_and_belongs_to_many :roles # rubocop:disable Rails/HasAndBelongsToMany - has_many :notifications, foreign_key: 'recipient_id' - has_many :sent_notifications, foreign_key: 'sender_id' + has_many :notifications, foreign_key: 'recipient_id', inverse_of: :recipient + has_many :sent_notifications, foreign_key: 'sender_id', inverse_of: :sender has_many :authentications, dependent: :destroy - has_many :photos - has_many :requested_crops, class_name: 'Crop', foreign_key: 'requester_id', dependent: :nullify + has_many :photos, inverse_of: :owner + has_many :requested_crops, class_name: 'Crop', foreign_key: 'requester_id', dependent: :nullify, + inverse_of: :requester has_many :likes, dependent: :destroy - has_many :follows, class_name: "Follow", foreign_key: "follower_id", dependent: :destroy + has_many :follows, class_name: "Follow", foreign_key: "follower_id", dependent: :destroy, + inverse_of: :follower has_many :inverse_follows, class_name: "Follow", foreign_key: "followed_id", dependent: :destroy has_many :followed, through: :follows has_many :followers, through: :inverse_follows, source: :follower diff --git a/app/models/seed.rb b/app/models/seed.rb index 8d33e7ff8..1eae0db62 100644 --- a/app/models/seed.rb +++ b/app/models/seed.rb @@ -16,7 +16,8 @@ class Seed < ApplicationRecord belongs_to :parent_planting, class_name: 'Planting', foreign_key: 'parent_planting_id', required: false # parent has_many :child_plantings, class_name: 'Planting', - foreign_key: 'parent_seed_id', dependent: :nullify # children + foreign_key: 'parent_seed_id', dependent: :nullify, + inverse_of: :parent_planting # children # # Validations From fe2536c864b62f575045f5d4239344a767f2e5b8 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 8 Oct 2018 14:15:44 +1300 Subject: [PATCH 167/219] More spec updates --- spec/features/crops/crop_detail_page_spec.rb | 2 +- spec/features/seeds/misc_seeds_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/crops/crop_detail_page_spec.rb b/spec/features/crops/crop_detail_page_spec.rb index 62f80d37f..e4e3d05e0 100644 --- a/spec/features/crops/crop_detail_page_spec.rb +++ b/spec/features/crops/crop_detail_page_spec.rb @@ -193,7 +193,7 @@ feature "crop detail page", js: true do end it "predicts harvest" do - is_expected.to have_text("First harvest expected 20 days after planting") + is_expected.to have_text("First harvest expected\n20 days after planting") end end end diff --git a/spec/features/seeds/misc_seeds_spec.rb b/spec/features/seeds/misc_seeds_spec.rb index ec1946d2c..c1172fcb2 100644 --- a/spec/features/seeds/misc_seeds_spec.rb +++ b/spec/features/seeds/misc_seeds_spec.rb @@ -50,7 +50,7 @@ feature "seeds", js: true do scenario "view seeds with max and min days until maturity" do seed = create :seed, days_until_maturity_min: 5, days_until_maturity_max: 7 visit seed_path(seed) - expect(page).to have_content "Days until maturity: 5–7" + expect(page).to have_content "Days until maturity:\n5–7" end scenario "view seeds with only max days until maturity" do From aded3c00fcf1e89eff6e3f14b26f63415a5d2f00 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 8 Oct 2018 14:17:28 +1300 Subject: [PATCH 168/219] More spec updates --- spec/views/seeds/show.html.haml_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/views/seeds/show.html.haml_spec.rb b/spec/views/seeds/show.html.haml_spec.rb index fd2848f97..b94804142 100644 --- a/spec/views/seeds/show.html.haml_spec.rb +++ b/spec/views/seeds/show.html.haml_spec.rb @@ -26,7 +26,7 @@ describe "seeds/show" do it "shows tradable attributes" do render - rendered.should have_content "Will trade: locally" + rendered.should have_content "Will trade:\nlocally" end it "shows location of seed owner" do From 7a44e9499d3edc7d3434e84ebcaa5f1fba502331 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 8 Oct 2018 14:18:19 +1300 Subject: [PATCH 169/219] More markup updates to fit specs --- app/views/crops/edit.html.haml | 3 ++- app/views/seeds/index.rss.haml | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/crops/edit.html.haml b/app/views/crops/edit.html.haml index 1c497d838..1d9963547 100644 --- a/app/views/crops/edit.html.haml +++ b/app/views/crops/edit.html.haml @@ -9,7 +9,8 @@ Approved by #{link_to @crop.creator, @crop.creator}. - else %p - Added by #{link_to @crop.creator, @crop.creator} + Added by + = link_to @crop.creator, @crop.creator #{distance_of_time_in_words(@crop.created_at, Time.zone.now)} ago. - elsif @crop.approval_status == "pending" .alert.alert-danger diff --git a/app/views/seeds/index.rss.haml b/app/views/seeds/index.rss.haml index 36d542706..b2044de6e 100644 --- a/app/views/seeds/index.rss.haml +++ b/app/views/seeds/index.rss.haml @@ -17,8 +17,7 @@

Heirloom? #{seed.heirloom}

- if seed.tradable? %p - Will trade #{seed.tradable_to} from - = seed.owner.location ? seed.owner.location : 'unknown location' + Will trade #{seed.tradable_to} from #{seed.owner.location ? seed.owner.location : 'unknown location'} :escaped_markdown #{ strip_tags seed.description } From 6c2afb28048236a03d652669eea53d727f06c326 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 16 Oct 2018 10:16:45 +1300 Subject: [PATCH 170/219] Adding missing inverswe relationship alternatename.creator --- app/models/member.rb | 4 +++- spec/models/alternate_name_spec.rb | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/models/member.rb b/app/models/member.rb index 69f5e61b6..3bde53e53 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -22,10 +22,12 @@ class Member < ApplicationRecord has_many :photos, inverse_of: :owner has_many :requested_crops, class_name: 'Crop', foreign_key: 'requester_id', dependent: :nullify, inverse_of: :requester + has_many :created_alternate_names, class_name: 'AlternateName', foreign_key: 'creator_id', inverse_of: :creator has_many :likes, dependent: :destroy has_many :follows, class_name: "Follow", foreign_key: "follower_id", dependent: :destroy, inverse_of: :follower - has_many :inverse_follows, class_name: "Follow", foreign_key: "followed_id", dependent: :destroy + has_many :inverse_follows, class_name: "Follow", foreign_key: "followed_id", + dependent: :destroy, inverse_of: :followed has_many :followed, through: :follows has_many :followers, through: :inverse_follows, source: :follower diff --git a/spec/models/alternate_name_spec.rb b/spec/models/alternate_name_spec.rb index f358a9fca..a76749130 100644 --- a/spec/models/alternate_name_spec.rb +++ b/spec/models/alternate_name_spec.rb @@ -18,4 +18,14 @@ describe AlternateName do expect(crop.alternate_names).to include an expect(crop.alternate_names).to include an2 end + + describe 'relationships' do + let(:alternate_name) { FactoryBot.create :alternate_name, crop: crop, creator: member } + let(:crop) { FactoryBot.create :crop } + let(:member) { FactoryBot.create :member } + + it { expect(alternate_name.crop).to eq crop } + it { expect(alternate_name.creator).to eq member } + it { expect(member.created_alternate_names).to eq [alternate_name] } + end end From fc0a941fb6e9ed441b7b3c6d322829046051fff6 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 16 Oct 2018 11:32:03 +1300 Subject: [PATCH 171/219] Adding more inverse relationships --- app/models/crop.rb | 8 ++++---- app/models/follow.rb | 4 ++-- app/models/member.rb | 15 ++++++++++++--- app/models/notification.rb | 4 ++-- app/models/post.rb | 2 +- app/models/scientific_name.rb | 2 +- app/models/seed.rb | 2 +- 7 files changed, 23 insertions(+), 14 deletions(-) diff --git a/app/models/crop.rb b/app/models/crop.rb index ba4787b97..829a3fc6a 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -16,11 +16,11 @@ class Crop < ApplicationRecord has_many :harvests, dependent: :destroy has_many :photos, through: :plantings has_many :plant_parts, -> { distinct.order("plant_parts.name") }, through: :harvests - belongs_to :creator, class_name: 'Member', optional: true - belongs_to :requester, class_name: 'Member', optional: true - belongs_to :parent, class_name: 'Crop', optional: true + belongs_to :creator, class_name: 'Member', optional: true, inverse_of: :created_crops + belongs_to :requester, class_name: 'Member', optional: true, inverse_of: :requestd_crops + belongs_to :parent, class_name: 'Crop', optional: true, inverse_of: :varieties has_many :varieties, class_name: 'Crop', foreign_key: 'parent_id', dependent: :nullify, inverse_of: :parent - has_and_belongs_to_many :posts + has_and_belongs_to_many :posts # rubocop:disable Rails/HasAndBelongsToMany ## ## Scopes diff --git a/app/models/follow.rb b/app/models/follow.rb index 34028857b..a70fd15f6 100644 --- a/app/models/follow.rb +++ b/app/models/follow.rb @@ -1,6 +1,6 @@ class Follow < ApplicationRecord - belongs_to :follower, class_name: "Member" - belongs_to :followed, class_name: "Member" + belongs_to :follower, class_name: "Member", inverse_of: :follows + belongs_to :followed, class_name: "Member", inverse_of: :inverse_follows validates :follower_id, uniqueness: { scope: :followed_id } after_create do diff --git a/app/models/member.rb b/app/models/member.rb index 3bde53e53..3a997840d 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -20,10 +20,10 @@ class Member < ApplicationRecord has_many :sent_notifications, foreign_key: 'sender_id', inverse_of: :sender has_many :authentications, dependent: :destroy has_many :photos, inverse_of: :owner - has_many :requested_crops, class_name: 'Crop', foreign_key: 'requester_id', dependent: :nullify, - inverse_of: :requester - has_many :created_alternate_names, class_name: 'AlternateName', foreign_key: 'creator_id', inverse_of: :creator has_many :likes, dependent: :destroy + + # + # Following other members has_many :follows, class_name: "Follow", foreign_key: "follower_id", dependent: :destroy, inverse_of: :follower has_many :inverse_follows, class_name: "Follow", foreign_key: "followed_id", @@ -31,6 +31,15 @@ class Member < ApplicationRecord has_many :followed, through: :follows has_many :followers, through: :inverse_follows, source: :follower + # + # Global data records this member created + has_many :requested_crops, class_name: 'Crop', foreign_key: 'requester_id', dependent: :nullify, + inverse_of: :requester + has_many :created_crops, class_name: 'Crop', foreign_key: 'creator_id', dependent: :nullify, + inverse_of: :creator + has_many :created_alternate_names, class_name: 'AlternateName', foreign_key: 'creator_id', inverse_of: :creator + has_many :created_scientific_names, class_name: 'ScientificName', foreign_key: 'creator_id', inverse_of: :creator + # # Scopes scope :confirmed, -> { where.not(confirmed_at: nil) } diff --git a/app/models/notification.rb b/app/models/notification.rb index c9f915420..d4e8db676 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -1,6 +1,6 @@ class Notification < ApplicationRecord - belongs_to :sender, class_name: 'Member' - belongs_to :recipient, class_name: 'Member' + belongs_to :sender, class_name: 'Member', inverse_of: :sent_notifications + belongs_to :recipient, class_name: 'Member', inverse_of: :notifications belongs_to :post, optional: true validates :subject, length: { maximum: 255 } diff --git a/app/models/post.rb b/app/models/post.rb index bde34cfe3..9e570b845 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -5,7 +5,7 @@ class Post < ApplicationRecord # # Relationships - belongs_to :author, class_name: 'Member' + belongs_to :author, class_name: 'Member', inverse_of: :posts belongs_to :forum, optional: true has_many :comments, dependent: :destroy has_and_belongs_to_many :crops # rubocop:disable Rails/HasAndBelongsToMany diff --git a/app/models/scientific_name.rb b/app/models/scientific_name.rb index 812b696b8..7b20d2e9e 100644 --- a/app/models/scientific_name.rb +++ b/app/models/scientific_name.rb @@ -1,7 +1,7 @@ class ScientificName < ApplicationRecord after_commit { |sn| sn.crop.__elasticsearch__.index_document if sn.crop && ENV['GROWSTUFF_ELASTICSEARCH'] == "true" } belongs_to :crop - belongs_to :creator, class_name: 'Member' + belongs_to :creator, class_name: 'Member', inverse_of: :created_scientific_names validates :name, presence: true validates :crop, presence: true end diff --git a/app/models/seed.rb b/app/models/seed.rb index 34c0d9e78..d9a2261d2 100644 --- a/app/models/seed.rb +++ b/app/models/seed.rb @@ -14,7 +14,7 @@ class Seed < ApplicationRecord # Relationships belongs_to :crop belongs_to :parent_planting, class_name: 'Planting', foreign_key: 'parent_planting_id', - required: false # parent + required: false, inverse_of: :child_seeds # parent has_many :child_plantings, class_name: 'Planting', foreign_key: 'parent_seed_id', dependent: :nullify, inverse_of: :parent_seed # children From 0a65e57d5378b5b617b3acc0077255642eea5e32 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 16 Oct 2018 12:42:15 +1300 Subject: [PATCH 172/219] Fixed up text in spec --- spec/features/shared_examples/append_date.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/shared_examples/append_date.rb b/spec/features/shared_examples/append_date.rb index 74c7f8497..b2c19e5f1 100644 --- a/spec/features/shared_examples/append_date.rb +++ b/spec/features/shared_examples/append_date.rb @@ -16,6 +16,6 @@ shared_examples "append date" do scenario "Confirming without selecting date" do click_link link_text click_link "Confirm without date" - expect(page).to have_content("Finished:\nYes (no date specified) ") + expect(page).to have_content("Finished:\nYes (no date specified)") end end From c1dabfc3d33dcd870253eedd2f8ede369cb00aa8 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 16 Oct 2018 12:42:49 +1300 Subject: [PATCH 173/219] Typo fix, requestd_crops to requested_crops --- app/models/crop.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/crop.rb b/app/models/crop.rb index 829a3fc6a..525e3fb8d 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -17,7 +17,7 @@ class Crop < ApplicationRecord has_many :photos, through: :plantings has_many :plant_parts, -> { distinct.order("plant_parts.name") }, through: :harvests belongs_to :creator, class_name: 'Member', optional: true, inverse_of: :created_crops - belongs_to :requester, class_name: 'Member', optional: true, inverse_of: :requestd_crops + belongs_to :requester, class_name: 'Member', optional: true, inverse_of: :requested_crops belongs_to :parent, class_name: 'Crop', optional: true, inverse_of: :varieties has_many :varieties, class_name: 'Crop', foreign_key: 'parent_id', dependent: :nullify, inverse_of: :parent has_and_belongs_to_many :posts # rubocop:disable Rails/HasAndBelongsToMany From 03da26f949c11e54da79bac599e04a84c58b5774 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 16 Oct 2018 12:45:18 +1300 Subject: [PATCH 174/219] Upgrade nokogiri --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 0a7379bff..8155116a6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -298,7 +298,7 @@ GEM multipart-post (2.0.0) newrelic_rpm (5.4.0.347) nio4r (2.3.1) - nokogiri (1.8.4) + nokogiri (1.8.5) mini_portile2 (~> 2.3.0) oauth (0.5.4) oauth2 (1.4.0) From 0d3588a2b25f4680aa51ca0d44c30b78d7a82fc0 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 16 Oct 2018 12:47:05 +1300 Subject: [PATCH 175/219] Add another inverse_of created_alternate_names --- app/models/alternate_name.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/alternate_name.rb b/app/models/alternate_name.rb index 0d7f12198..94e404b49 100644 --- a/app/models/alternate_name.rb +++ b/app/models/alternate_name.rb @@ -1,7 +1,7 @@ class AlternateName < ApplicationRecord after_commit { |an| an.crop.__elasticsearch__.index_document if an.crop && ENV['GROWSTUFF_ELASTICSEARCH'] == "true" } belongs_to :crop - belongs_to :creator, class_name: 'Member' + belongs_to :creator, class_name: 'Member', inverse_of: :created_alternate_names validates :name, presence: true validates :crop, presence: true end From de2175ce16242dd1b9f9e2dd95d01c018302fd7a Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 16 Oct 2018 13:20:03 +1300 Subject: [PATCH 176/219] More inverse relationships defined --- app/models/garden.rb | 2 +- app/models/photo.rb | 3 ++- app/models/photographing.rb | 2 +- app/models/planting.rb | 12 ++++++++---- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/models/garden.rb b/app/models/garden.rb index 9687982cb..01466120f 100644 --- a/app/models/garden.rb +++ b/app/models/garden.rb @@ -62,7 +62,7 @@ class Garden < ApplicationRecord unique_plantings = [] seen_crops = [] - plantings.order(created_at: :desc).includes(:garden, :crop, :owner, :harvests).each do |p| + plantings.includes(:garden, :crop, :owner, :harvests).order(created_at: :desc).each do |p| unless seen_crops.include?(p.crop) unique_plantings.push(p) seen_crops.push(p.crop) diff --git a/app/models/photo.rb b/app/models/photo.rb index 69a30d245..f3636760c 100644 --- a/app/models/photo.rb +++ b/app/models/photo.rb @@ -3,7 +3,8 @@ class Photo < ApplicationRecord PHOTO_CAPABLE = %w(Garden Planting Harvest Seed).freeze - has_many :photographings, foreign_key: :photo_id, dependent: :destroy + has_many :photographings, foreign_key: :photo_id, dependent: :destroy, inverse_of: :photo + # creates a relationship for each assignee type PHOTO_CAPABLE.each do |type| has_many type.downcase.pluralize.to_s.to_sym, diff --git a/app/models/photographing.rb b/app/models/photographing.rb index 040817611..b62ed10e6 100644 --- a/app/models/photographing.rb +++ b/app/models/photographing.rb @@ -1,5 +1,5 @@ class Photographing < ApplicationRecord - belongs_to :photo + belongs_to :photo, inverse_of: :photographings belongs_to :photographable, polymorphic: true validate :photo_and_item_have_same_owner diff --git a/app/models/planting.rb b/app/models/planting.rb index 61b75e326..27f99870b 100644 --- a/app/models/planting.rb +++ b/app/models/planting.rb @@ -21,10 +21,14 @@ class Planting < ApplicationRecord # # Ancestry of food - belongs_to :parent_seed, class_name: 'Seed', foreign_key: 'parent_seed_id', - required: false # parent - has_many :child_seeds, class_name: 'Seed', - foreign_key: 'parent_planting_id', dependent: :nullify # children + belongs_to :parent_seed, class_name: 'Seed', # parent + foreign_key: 'parent_seed_id', + required: false, + inverse_of: :child_plantings + has_many :child_seeds, class_name: 'Seed', # children + foreign_key: 'parent_planting_id', + inverse_of: :parent_planting, + dependent: :nullify ## ## Scopes From 510124758d50c1278f59c933abda7f54e3a53151 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 16 Oct 2018 13:26:54 +1300 Subject: [PATCH 177/219] Inverse relation ships --- app/models/concerns/likeable.rb | 2 +- app/models/concerns/ownable.rb | 3 ++- app/models/concerns/photo_capable.rb | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/models/concerns/likeable.rb b/app/models/concerns/likeable.rb index e898b8bab..328cf3153 100644 --- a/app/models/concerns/likeable.rb +++ b/app/models/concerns/likeable.rb @@ -2,7 +2,7 @@ module Likeable extend ActiveSupport::Concern included do - has_many :likes, as: :likeable, dependent: :destroy + has_many :likes, as: :likeable, inverse_of: :likeable, dependent: :destroy has_many :members, through: :likes end end diff --git a/app/models/concerns/ownable.rb b/app/models/concerns/ownable.rb index b9f229973..eea4c9ac7 100644 --- a/app/models/concerns/ownable.rb +++ b/app/models/concerns/ownable.rb @@ -2,6 +2,7 @@ module Ownable extend ActiveSupport::Concern included do - belongs_to :owner, class_name: 'Member', foreign_key: 'owner_id', counter_cache: true + belongs_to :owner, class_name: 'Member', # rubocop:disable Rails/InverseOf + foreign_key: 'owner_id', counter_cache: true end end diff --git a/app/models/concerns/photo_capable.rb b/app/models/concerns/photo_capable.rb index 758569c35..3ae7303d9 100644 --- a/app/models/concerns/photo_capable.rb +++ b/app/models/concerns/photo_capable.rb @@ -2,7 +2,7 @@ module PhotoCapable extend ActiveSupport::Concern included do - has_many :photographings, as: :photographable, dependent: :destroy + has_many :photographings, as: :photographable, dependent: :destroy, inverse_of: :photographable has_many :photos, through: :photographings, as: :photographable scope :has_photos, -> { includes(:photos).where.not(photos: { id: nil }) } From ec4c6f13aa7754a35ef6755a0e80d3b5ae29a395 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 16 Oct 2018 13:56:10 +1300 Subject: [PATCH 178/219] Updating new seed view specs --- spec/views/seeds/new.html.haml_spec.rb | 44 +++++++++++--------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/spec/views/seeds/new.html.haml_spec.rb b/spec/views/seeds/new.html.haml_spec.rb index 5c5cf3ae4..fdea300fe 100644 --- a/spec/views/seeds/new.html.haml_spec.rb +++ b/spec/views/seeds/new.html.haml_spec.rb @@ -1,16 +1,17 @@ require 'rails_helper' describe "seeds/new" do + let!(:seed) { FactoryBot.create(:seed, owner: member) } + let!(:member) { FactoryBot.create(:member) } + before(:each) do - @member = FactoryBot.create(:member) - sign_in @member + sign_in member controller.stub(:current_user) { @member } - @seed1 = FactoryBot.create(:seed, owner: @member) - assign(:seed, @seed1) + assign(:seed, seed) + render end it "renders new seed form" do - render assert_select "form", action: seeds_path, method: "post" do assert_select "input#crop", class: "ui-autocomplete-input" assert_select "input#seed_crop_id", name: "seed[crop_id]" @@ -20,30 +21,23 @@ describe "seeds/new" do end end - it 'reminds you to set your location' do - render - rendered.should have_content "Don't forget to set your location." - assert_select "a", text: "set your location" + context 'member has no location' do + describe 'reminds you to set your location' do + it { expect(rendered).to have_content "Don't forget to\nset your location." } + it { expect(rendered).to have_link "set your location" } + end end - context 'member has location' do - before(:each) do - @member = FactoryBot.create(:london_member) - sign_in @member - controller.stub(:current_user) { @member } - @seed1 = FactoryBot.create(:seed, owner: @member) - assign(:seed, @seed1) + context 'when member has location' do + let!(:member) { FactoryBot.create(:london_member) } + + describe 'shows the location' do + it { expect(rendered).to have_text "from\n#{member.location}." } + it { expect(rendered).to have_link(member.location, href: place_path(member.location)) } end - it 'shows the location' do - render - rendered.should have_content "from #{@member.location}." - assert_select 'a', href: place_path(@member.location) - end - - it 'links to change location' do - render - assert_select "a", text: "Change your location." + it 'link to change location' do + expect(rendered).to have_link("Change your location.") end end end From 92939ca2043a63f0f50a1667b8ca2824b4dace98 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 16 Oct 2018 14:08:37 +1300 Subject: [PATCH 179/219] Spec updates --- app/views/alternate_names/edit.html.haml | 2 +- app/views/scientific_names/edit.html.haml | 2 +- spec/features/planting_reminder_spec.rb | 2 +- .../scientific_names/edit.html.haml_spec.rb | 16 +++++++++------- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/app/views/alternate_names/edit.html.haml b/app/views/alternate_names/edit.html.haml index 0818bb0ff..35728110a 100644 --- a/app/views/alternate_names/edit.html.haml +++ b/app/views/alternate_names/edit.html.haml @@ -2,7 +2,7 @@ %p Added by - = @alternate_name.creator + = link_to @alternate_name.creator, @alternate_name.creator = distance_of_time_in_words(@alternate_name.created_at, Time.zone.now) ago. diff --git a/app/views/scientific_names/edit.html.haml b/app/views/scientific_names/edit.html.haml index db7f9bf71..0cdebe6cf 100644 --- a/app/views/scientific_names/edit.html.haml +++ b/app/views/scientific_names/edit.html.haml @@ -2,7 +2,7 @@ %p Added by - = @scientific_name.creator + = link_to @scientific_name.creator, @scientific_name.creator = distance_of_time_in_words(@scientific_name.created_at, Time.zone.now) ago. diff --git a/spec/features/planting_reminder_spec.rb b/spec/features/planting_reminder_spec.rb index 3b407c3df..bcb86d576 100644 --- a/spec/features/planting_reminder_spec.rb +++ b/spec/features/planting_reminder_spec.rb @@ -41,7 +41,7 @@ feature "Planting reminder email", :js do context "when member has no harvests" do scenario "tells you to tracking plantings" do - expect(mail).to have_content "Get started now by tracking your first harvest" + expect(mail).to have_content "Get started now\nby tracking your first harvest" end scenario "doesn't list plantings" do diff --git a/spec/views/scientific_names/edit.html.haml_spec.rb b/spec/views/scientific_names/edit.html.haml_spec.rb index ecaa2056f..2d6fd73a5 100644 --- a/spec/views/scientific_names/edit.html.haml_spec.rb +++ b/spec/views/scientific_names/edit.html.haml_spec.rb @@ -2,21 +2,23 @@ require 'rails_helper' describe "scientific_names/edit" do context "logged in" do + let(:member) { FactoryBot.create(:member) } + let(:scientific_name) { FactoryBot.create(:zea_mays, creator: member) } + before(:each) do - @member = FactoryBot.create(:member) - sign_in @member - controller.stub(:current_user) { @member } - @scientific_name = assign(:scientific_name, - FactoryBot.create(:zea_mays)) + sign_in member + controller.stub(:current_user) { member } + assign(:scientific_name, scientific_name) render end it "shows the creator" do - rendered.should have_content "Added by #{@scientific_name.creator} less than a minute ago." + expect(rendered).to have_content "Added by\n#{member}\nless than a minute\nago." end + it { expect(rendered).to have_link member } it "renders the edit scientific_name form" do - assert_select "form", action: scientific_names_path(@scientific_name), method: "post" do + assert_select "form", action: scientific_names_path(scientific_name), method: "post" do assert_select "input#scientific_name_name", name: "scientific_name[scientific_name]" assert_select "select#scientific_name_crop_id", name: "scientific_name[crop_id]" end From fb156ec43894620d667cc127debaf92db1482497 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 16 Oct 2018 14:20:15 +1300 Subject: [PATCH 180/219] Updating edit seeds spec --- spec/features/seeds/misc_seeds_spec.rb | 88 ++++++++++++++------------ 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/spec/features/seeds/misc_seeds_spec.rb b/spec/features/seeds/misc_seeds_spec.rb index c1172fcb2..3687ca630 100644 --- a/spec/features/seeds/misc_seeds_spec.rb +++ b/spec/features/seeds/misc_seeds_spec.rb @@ -1,31 +1,38 @@ require 'rails_helper' feature "seeds", js: true do + let(:member) { create :member } + context "signed in user" do - let(:member) { create :member } let(:crop) { create :crop } background { login_as member } - scenario "button on index to edit seed" do - seed = create :seed, owner: member - visit seeds_path - click_link "edit_seed_glyphicon" - expect(current_path).to eq edit_seed_path(seed) - expect(page).to have_content 'Editing seeds' + describe "button on index to edit seed" do + let!(:seed) { create :seed, owner: member } + before do + visit seeds_path + click_link "edit_seed_glyphicon" + end + it { expect(current_path).to eq edit_seed_path(seed) } + it { expect(page).to have_content 'Editing seeds' } end - scenario "button on front page to add seeds" do - visit root_path - click_link "Add seeds" - expect(current_path).to eq new_seed_path - expect(page).to have_content 'Add seeds' + describe "button on front page to add seeds" do + before do + visit root_path + click_link "Add seeds" + end + it { expect(current_path).to eq new_seed_path } + it { expect(page).to have_content 'Add seeds' } end - scenario "Clicking link to owner's profile" do - visit seeds_by_owner_path(member) - click_link "View #{member}'s profile >>" - expect(current_path).to eq member_path(member) + describe "Clicking link to owner's profile" do + before do + visit seeds_by_owner_path(member) + click_link "View #{member}'s profile >>" + end + it { expect(current_path).to eq member_path(member) } end # actually adding seeds is in spec/features/seeds_new_spec.rb @@ -40,35 +47,36 @@ feature "seeds", js: true do expect(current_path).to eq seed_path(seed) end - scenario "delete seeds" do - seed = create :seed, owner: member - visit seed_path(seed) - click_link 'Delete' - expect(current_path).to eq seeds_path + describe "delete seeds" do + let(:seed) { FactoryBot.create :seed, owner: member } + before do + visit seed_path(seed) + click_link 'Delete' + end + it { expect(current_path).to eq seeds_path } end - scenario "view seeds with max and min days until maturity" do - seed = create :seed, days_until_maturity_min: 5, days_until_maturity_max: 7 - visit seed_path(seed) - expect(page).to have_content "Days until maturity:\n5–7" - end + describe '#show' do + before { visit seed_path(seed) } + describe "view seeds with max and min days until maturity" do + let(:seed) { FactoryBot.create :seed, days_until_maturity_min: 5, days_until_maturity_max: 7 } + it { expect(page).to have_content "Days until maturity:\n5–7" } + end - scenario "view seeds with only max days until maturity" do - seed = create :seed, days_until_maturity_max: 7 - visit seed_path(seed) - expect(page).to have_content "Days until maturity: 7" - end + describe "view seeds with only max days until maturity" do + let(:seed) { FactoryBot.create :seed, days_until_maturity_max: 7 } + it { expect(page).to have_content "Days until maturity:\n7" } + end - scenario "view seeds with only min days until maturity" do - seed = create :seed, days_until_maturity_min: 5 - visit seed_path(seed) - expect(page).to have_content "Days until maturity: 5" - end + describe "view seeds with only min days until maturity" do + let(:seed) { FactoryBot.create :seed, days_until_maturity_min: 5 } + it { expect(page).to have_content "Days until maturity:\n5" } + end - scenario "view seeds with neither max nor min days until maturity" do - seed = create :seed - visit seed_path(seed) - expect(page).to have_content "Days until maturity: unknown" + describe "view seeds with neither max nor min days until maturity" do + let(:seed) { FactoryBot.create :seed } + it { expect(page).to have_content "Days until maturity:\nunknown" } + end end end end From 918f73d0f029942e26f9baaf6da205285bec4808 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 16 Oct 2018 14:29:01 +1300 Subject: [PATCH 181/219] Update crop edit spec --- spec/views/crops/edit.html.haml_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/views/crops/edit.html.haml_spec.rb b/spec/views/crops/edit.html.haml_spec.rb index 19fac21af..6dbae67d4 100644 --- a/spec/views/crops/edit.html.haml_spec.rb +++ b/spec/views/crops/edit.html.haml_spec.rb @@ -14,6 +14,6 @@ describe "crops/edit" do end it "shows the creator" do - rendered.should have_content "Added by #{@crop.creator} less than a minute ago." + expect(rendered).to have_content "Added by\n#{@crop.creator}\nless than a minute\nago." end end From ac8dff2a28e2fb15c153f09874529bdc85365e9e Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 16 Oct 2018 14:29:44 +1300 Subject: [PATCH 182/219] Update new post spec --- spec/views/posts/new.html.haml_spec.rb | 44 ++++++++++++-------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/spec/views/posts/new.html.haml_spec.rb b/spec/views/posts/new.html.haml_spec.rb index fa344e55b..9bf53e563 100644 --- a/spec/views/posts/new.html.haml_spec.rb +++ b/spec/views/posts/new.html.haml_spec.rb @@ -1,16 +1,16 @@ require 'rails_helper' describe "posts/new" do + let(:author) { FactoryBot.create(:member) } before(:each) do - @author = FactoryBot.create(:member) - assign(:post, FactoryBot.create(:post, author: @author)) + assign(:post, FactoryBot.create(:post, author: author)) # assign(:forum, Forum.new) - sign_in @author - controller.stub(:current_user) { @author } + sign_in author + controller.stub(:current_user) { author } + render end it "renders new post form" do - render assert_select "form", action: posts_path, method: "post" do assert_select "input#post_subject", name: "post[subject]" assert_select "textarea#post_body", name: "post[body]" @@ -18,44 +18,42 @@ describe "posts/new" do end it 'no hidden forum field' do - render assert_select "input#post_forum_id[type=hidden]", false end it 'no forum mentioned' do - render - rendered.should_not have_content "This post will be posted in the forum" + expect(rendered).not_to have_content "This post will be posted in the forum" end it "asks what's going on in your garden" do - render - rendered.should have_content "What's going on in your food garden?" + expect(rendered).to have_content "What's going on in your food garden?" + end + + it 'shows markdown help' do + expect(rendered).to have_content 'Markdown' end context "forum specified" do + let(:forum) { FactoryBot.create(:forum) } + before(:each) do - @forum = assign(:forum, FactoryBot.create(:forum)) - assign(:post, FactoryBot.create(:post, forum: @forum)) + assign(:forum, forum) + assign(:post, FactoryBot.create(:post, forum: forum)) render end it 'creates a hidden field' do - assert_select "input#post_forum_id[type='hidden'][value='#{@forum.id}']" + assert_select "input#post_forum_id[type='hidden'][value='#{forum.id}']" end it 'tells the user what forum it will be posted in' do - rendered.should have_content "This post will be posted in the forum #{@forum.name}" + expect(rendered).to have_content "This post will be posted in the forum\n#{forum.name}" end + it { expect(rendered).to have_link forum.name } - it "asks what's going on generally" do - render - rendered.should_not have_content "What's going on in your food garden?" - rendered.should have_content "What's up?" + describe "asks what's going on generally" do + it { expect(rendered).to have_content "What's going on in your food garden?" } + it { expect(rendered).to have_content "What's up?" } end end - - it 'shows markdown help' do - render - rendered.should have_content 'Markdown' - end end From cf11cbd7d248cb4488e78df3599e0492febb8a3c Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Tue, 16 Oct 2018 15:11:46 +1300 Subject: [PATCH 183/219] Update seeds specs --- spec/views/seeds/show.html.haml_spec.rb | 73 +++++++++++++------------ 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/spec/views/seeds/show.html.haml_spec.rb b/spec/views/seeds/show.html.haml_spec.rb index b94804142..7c7604001 100644 --- a/spec/views/seeds/show.html.haml_spec.rb +++ b/spec/views/seeds/show.html.haml_spec.rb @@ -1,62 +1,65 @@ require 'rails_helper' describe "seeds/show" do + let(:seed) { FactoryBot.create(:seed) } before(:each) do controller.stub(:current_user) { nil } - @seed = FactoryBot.create(:seed) - assign(:seed, @seed) - assign(:photos, @seed.photos.paginate(page: 1)) + assign(:seed, seed) + assign(:photos, seed.photos.paginate(page: 1)) + render end it "renders attributes in

" do - render - rendered.should have_content @seed.crop.name + expect(rendered).to have_content seed.crop.name end context "tradable" do - before(:each) do - @owner = FactoryBot.create(:london_member) - assign(:seed, FactoryBot.create(:tradable_seed, - owner: @owner)) - # note current_member is not the owner of this seed - @member = FactoryBot.create(:member) - sign_in @member - controller.stub(:current_user) { @member } - end + context 'with location' do + let!(:owner) { FactoryBot.create(:london_member) } + let!(:seed) { FactoryBot.create(:tradable_seed, owner: owner) } + let!(:member) { FactoryBot.create(:member) } - it "shows tradable attributes" do - render - rendered.should have_content "Will trade:\nlocally" - end + before(:each) do + assign(:seed, seed) + # note current_member is not the owner of this seed + sign_in member + controller.stub(:current_user) { member } + render + end - it "shows location of seed owner" do - render - rendered.should have_content @owner.location - assert_select 'a', href: place_path(@owner.location) + it "shows tradable attributes" do + expect(rendered).to have_content "Will trade:\n\nlocally" + end + + it "shows button to send message" do + expect(rendered).to have_content "Request seeds" + end + + describe "shows location of seed owner" do + it { expect(rendered).to have_content owner.location } + it { expect(rendered).to have_link seed.owner.location, href: place_path(seed.owner.location, anchor: "seeds") } + end end context 'with no location' do + # no location + let(:owner) { FactoryBot.create(:member) } + let!(:seed) { FactoryBot.create(:tradable_seed, owner: owner) } + before(:each) do - @owner = FactoryBot.create(:member) # no location - sign_in @owner - controller.stub(:current_user) { @owner } - assign(:seed, FactoryBot.create(:tradable_seed, owner: @owner)) + sign_in owner + controller.stub(:current_user) { owner } + assign(:seed, seed) + render end it 'says "from unspecified location"' do - render - rendered.should have_content "(from unspecified location)" + expect(rendered).to have_content "(from unspecified location)" end it "links to profile to set location" do - render - assert_select "a[href='#{url_for(edit_member_registration_path)}']", text: "Set Location" + expect(rendered).to have_link("Set Location") # , href: edit_member_registration_path) end end - - it "shows button to send message" do - render - rendered.should have_content "Request seeds" - end end end From 8348530e22e646e48a587b100cc4579552f06c5c Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 12:58:22 +1300 Subject: [PATCH 184/219] Tell capybara to normalise whitespace --- config/environments/test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/config/environments/test.rb b/config/environments/test.rb index 617338127..cbf6101f0 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -98,6 +98,7 @@ Geocoder::Lookup::Test.add_stub("Tatooine", []) Capybara.configure do |config| config.always_include_port = true + config.default_normalize_ws = true end OmniAuth.config.test_mode = true From 0c78791d0198f49081cf17d2da8c4bd799415b8f Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 13:22:42 +1300 Subject: [PATCH 185/219] Remove unnecesary whitesapce in specs --- spec/features/crops/crop_detail_page_spec.rb | 2 +- spec/features/planting_reminder_spec.rb | 2 +- spec/features/plantings/planting_a_crop_spec.rb | 4 ++-- spec/features/seeds/adding_seeds_spec.rb | 6 +++--- spec/features/seeds/misc_seeds_spec.rb | 8 ++++---- spec/features/shared_examples/append_date.rb | 4 ++-- spec/views/crops/edit.html.haml_spec.rb | 2 +- spec/views/posts/new.html.haml_spec.rb | 2 +- spec/views/scientific_names/edit.html.haml_spec.rb | 2 +- spec/views/seeds/new.html.haml_spec.rb | 4 ++-- spec/views/seeds/show.html.haml_spec.rb | 2 +- 11 files changed, 19 insertions(+), 19 deletions(-) diff --git a/spec/features/crops/crop_detail_page_spec.rb b/spec/features/crops/crop_detail_page_spec.rb index e4e3d05e0..62f80d37f 100644 --- a/spec/features/crops/crop_detail_page_spec.rb +++ b/spec/features/crops/crop_detail_page_spec.rb @@ -193,7 +193,7 @@ feature "crop detail page", js: true do end it "predicts harvest" do - is_expected.to have_text("First harvest expected\n20 days after planting") + is_expected.to have_text("First harvest expected 20 days after planting") end end end diff --git a/spec/features/planting_reminder_spec.rb b/spec/features/planting_reminder_spec.rb index bcb86d576..3b407c3df 100644 --- a/spec/features/planting_reminder_spec.rb +++ b/spec/features/planting_reminder_spec.rb @@ -41,7 +41,7 @@ feature "Planting reminder email", :js do context "when member has no harvests" do scenario "tells you to tracking plantings" do - expect(mail).to have_content "Get started now\nby tracking your first harvest" + expect(mail).to have_content "Get started now by tracking your first harvest" end scenario "doesn't list plantings" do diff --git a/spec/features/plantings/planting_a_crop_spec.rb b/spec/features/plantings/planting_a_crop_spec.rb index 3da396ee0..1d8b7f745 100644 --- a/spec/features/plantings/planting_a_crop_spec.rb +++ b/spec/features/plantings/planting_a_crop_spec.rb @@ -211,7 +211,7 @@ feature "Planting a crop", :js, :elasticsearch do click_button "Save" end expect(page).to have_content "planting was successfully created" - expect(page).to have_content "Finished:\nAugust 30, 2014" + expect(page).to have_content "Finished: August 30, 2014" # shouldn't be on the page visit plantings_path @@ -232,7 +232,7 @@ feature "Planting a crop", :js, :elasticsearch do end end it { expect(page).to have_content "planting was successfully created" } - it { expect(page).to have_content "Finished:\nYes (no date specified)" } + it { expect(page).to have_content "Finished: Yes (no date specified)" } it { expect(page).to have_content "100%" } end diff --git a/spec/features/seeds/adding_seeds_spec.rb b/spec/features/seeds/adding_seeds_spec.rb index 8a875cc85..67a8b7717 100644 --- a/spec/features/seeds/adding_seeds_spec.rb +++ b/spec/features/seeds/adding_seeds_spec.rb @@ -48,11 +48,11 @@ feature "Seeds", :js, :elasticsearch do end end it { expect(page).to have_content "Successfully added maize seed to your stash" } - it { expect(page).to have_content "Quantity:\n42" } - it { expect(page).to have_content "Days until maturity:\n999–1999" } + it { expect(page).to have_content "Quantity: 42" } + it { expect(page).to have_content "Days until maturity: 999–1999" } it { expect(page).to have_content "certified organic" } it { expect(page).to have_content "non-certified GMO-free" } - it { expect(page).to have_content "Heirloom?\nheirloom" } + it { expect(page).to have_content "Heirloom? heirloom" } it { expect(page).to have_content "It's killer." } end diff --git a/spec/features/seeds/misc_seeds_spec.rb b/spec/features/seeds/misc_seeds_spec.rb index 3687ca630..e095ed390 100644 --- a/spec/features/seeds/misc_seeds_spec.rb +++ b/spec/features/seeds/misc_seeds_spec.rb @@ -60,22 +60,22 @@ feature "seeds", js: true do before { visit seed_path(seed) } describe "view seeds with max and min days until maturity" do let(:seed) { FactoryBot.create :seed, days_until_maturity_min: 5, days_until_maturity_max: 7 } - it { expect(page).to have_content "Days until maturity:\n5–7" } + it { expect(page).to have_content "Days until maturity: 5–7" } end describe "view seeds with only max days until maturity" do let(:seed) { FactoryBot.create :seed, days_until_maturity_max: 7 } - it { expect(page).to have_content "Days until maturity:\n7" } + it { expect(page).to have_content "Days until maturity: 7" } end describe "view seeds with only min days until maturity" do let(:seed) { FactoryBot.create :seed, days_until_maturity_min: 5 } - it { expect(page).to have_content "Days until maturity:\n5" } + it { expect(page).to have_content "Days until maturity: 5" } end describe "view seeds with neither max nor min days until maturity" do let(:seed) { FactoryBot.create :seed } - it { expect(page).to have_content "Days until maturity:\nunknown" } + it { expect(page).to have_content "Days until maturity: unknown" } end end end diff --git a/spec/features/shared_examples/append_date.rb b/spec/features/shared_examples/append_date.rb index b2c19e5f1..fcb6d515b 100644 --- a/spec/features/shared_examples/append_date.rb +++ b/spec/features/shared_examples/append_date.rb @@ -10,12 +10,12 @@ shared_examples "append date" do expect(page).to have_content this_month.to_s find(".datepicker-days td.day", text: "21").click end - expect(page).to have_content "Finished:\n#{this_month} 21, #{this_year}" + expect(page).to have_content "Finished: #{this_month} 21, #{this_year}" end scenario "Confirming without selecting date" do click_link link_text click_link "Confirm without date" - expect(page).to have_content("Finished:\nYes (no date specified)") + expect(page).to have_content("Finished: Yes (no date specified)") end end diff --git a/spec/views/crops/edit.html.haml_spec.rb b/spec/views/crops/edit.html.haml_spec.rb index 6dbae67d4..f0d8e9251 100644 --- a/spec/views/crops/edit.html.haml_spec.rb +++ b/spec/views/crops/edit.html.haml_spec.rb @@ -14,6 +14,6 @@ describe "crops/edit" do end it "shows the creator" do - expect(rendered).to have_content "Added by\n#{@crop.creator}\nless than a minute\nago." + expect(rendered).to have_content "Added by #{@crop.creator} less than a minute ago." end end diff --git a/spec/views/posts/new.html.haml_spec.rb b/spec/views/posts/new.html.haml_spec.rb index 9bf53e563..6c6a2ae68 100644 --- a/spec/views/posts/new.html.haml_spec.rb +++ b/spec/views/posts/new.html.haml_spec.rb @@ -47,7 +47,7 @@ describe "posts/new" do end it 'tells the user what forum it will be posted in' do - expect(rendered).to have_content "This post will be posted in the forum\n#{forum.name}" + expect(rendered).to have_content "This post will be posted in the forum #{forum.name}" end it { expect(rendered).to have_link forum.name } diff --git a/spec/views/scientific_names/edit.html.haml_spec.rb b/spec/views/scientific_names/edit.html.haml_spec.rb index 2d6fd73a5..249cd249b 100644 --- a/spec/views/scientific_names/edit.html.haml_spec.rb +++ b/spec/views/scientific_names/edit.html.haml_spec.rb @@ -13,7 +13,7 @@ describe "scientific_names/edit" do end it "shows the creator" do - expect(rendered).to have_content "Added by\n#{member}\nless than a minute\nago." + expect(rendered).to have_content "Added by #{member} less than a minute ago." end it { expect(rendered).to have_link member } diff --git a/spec/views/seeds/new.html.haml_spec.rb b/spec/views/seeds/new.html.haml_spec.rb index fdea300fe..3b33cc6c4 100644 --- a/spec/views/seeds/new.html.haml_spec.rb +++ b/spec/views/seeds/new.html.haml_spec.rb @@ -23,7 +23,7 @@ describe "seeds/new" do context 'member has no location' do describe 'reminds you to set your location' do - it { expect(rendered).to have_content "Don't forget to\nset your location." } + it { expect(rendered).to have_content "Don't forget to set your location." } it { expect(rendered).to have_link "set your location" } end end @@ -32,7 +32,7 @@ describe "seeds/new" do let!(:member) { FactoryBot.create(:london_member) } describe 'shows the location' do - it { expect(rendered).to have_text "from\n#{member.location}." } + it { expect(rendered).to have_text "from #{member.location}." } it { expect(rendered).to have_link(member.location, href: place_path(member.location)) } end diff --git a/spec/views/seeds/show.html.haml_spec.rb b/spec/views/seeds/show.html.haml_spec.rb index 7c7604001..2ad38bdcf 100644 --- a/spec/views/seeds/show.html.haml_spec.rb +++ b/spec/views/seeds/show.html.haml_spec.rb @@ -28,7 +28,7 @@ describe "seeds/show" do end it "shows tradable attributes" do - expect(rendered).to have_content "Will trade:\n\nlocally" + expect(rendered).to have_content "Will trade: locally" end it "shows button to send message" do From 7e8aa7a435cda99a9a27f6be6db332b15214b58c Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 13:22:52 +1300 Subject: [PATCH 186/219] bundle update --- Gemfile.lock | 129 ++++++++++++++++++++++++++------------------------- 1 file changed, 67 insertions(+), 62 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 952bcd2ab..be2ea990f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -49,9 +49,9 @@ GEM active_link_to (1.0.5) actionpack addressable - active_median (0.1.4) - activerecord - active_utils (3.3.15) + active_median (0.2.2) + activerecord (>= 4.2) + active_utils (3.3.16) activesupport (>= 4.2) i18n activejob (5.1.4) @@ -72,7 +72,7 @@ GEM public_suffix (>= 2.0.2, < 4.0) arel (8.0.0) ast (2.4.0) - autoprefixer-rails (9.1.4) + autoprefixer-rails (9.4.3) execjs bcrypt (3.1.12) better_errors (2.5.0) @@ -88,23 +88,25 @@ GEM bootstrap-kaminari-views (0.0.5) kaminari (>= 0.13) rails (>= 3.1) - bootstrap-sass (3.3.7) + bootstrap-sass (3.4.0) autoprefixer-rails (>= 5.2.1) - sass (>= 3.3.4) - bootstrap_form (2.7.0) + sassc (>= 2.0.0) + bootstrap_form (4.0.0) + rails (>= 5.0) builder (3.2.3) bullet (5.9.0) activesupport (>= 3.0.0) uniform_notifier (~> 1.11) byebug (10.0.2) - cancancan (2.2.0) - capybara (3.7.2) + cancancan (2.3.0) + capybara (3.12.0) addressable mini_mime (>= 0.1.3) nokogiri (~> 1.8) rack (>= 1.6.0) rack-test (>= 0.6.3) - xpath (~> 3.1) + regexp_parser (~> 1.2) + xpath (~> 3.2) capybara-email (3.0.1) capybara (>= 2.4, < 4.0) mail @@ -128,7 +130,7 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.0.5) + concurrent-ruby (1.1.4) connection_pool (2.2.2) coveralls (0.8.19) json (>= 1.8, < 3) @@ -162,7 +164,7 @@ GEM elasticsearch-transport (6.1.0) faraday multi_json - erubi (1.7.1) + erubi (1.8.0) erubis (2.7.0) excon (0.62.0) execjs (2.7.0) @@ -173,13 +175,13 @@ GEM railties (>= 3.0.0) faker (1.9.1) i18n (>= 0.7) - faraday (0.12.2) + faraday (0.15.4) multipart-post (>= 1.2, < 3) ffi (1.9.25) figaro (1.1.1) thor (~> 0.14) flickraw (0.9.9) - font-awesome-sass (5.3.1) + font-awesome-sass (5.6.1) sassc (>= 1.11) friendly_id (5.2.4) activerecord (>= 4.0.0) @@ -213,7 +215,7 @@ GEM rake (>= 10, < 13) rubocop (>= 0.50.0) sysexits (~> 1.1) - hashie (3.5.7) + hashie (3.6.0) heroics (0.0.25) erubis (~> 2.0) excon @@ -225,17 +227,19 @@ GEM haml (>= 4.0, < 6) nokogiri (>= 1.6.0) ruby_parser (~> 3.5) - httparty (0.16.2) + httparty (0.16.3) + mime-types (~> 3.0) multi_xml (>= 0.5.2) i18n (0.9.5) concurrent-ruby (~> 1.0) - i18n-tasks (0.9.24) + i18n-tasks (0.9.28) activesupport (>= 4.0.2) ast (>= 2.1.0) erubi highline (>= 2.0.0) i18n parser (>= 2.2.3.0) + rails-i18n rainbow (>= 2.2.2, < 4.0) terminal-table (>= 1.5.1) jaro_winkler (1.5.1) @@ -253,7 +257,7 @@ GEM activerecord (>= 4.1) concurrent-ruby railties (>= 4.1) - jwt (1.5.6) + jwt (2.1.0) kaminari (1.1.1) activesupport (>= 4.1.0) kaminari-actionview (= 1.1.1) @@ -281,34 +285,34 @@ GEM loofah (2.2.3) crass (~> 1.0.2) nokogiri (>= 1.5.9) - mail (2.7.0) + mail (2.7.1) mini_mime (>= 0.1.1) memcachier (0.0.2) - method_source (0.9.0) + method_source (0.9.2) mime-types (3.2.2) mime-types-data (~> 3.2015) mime-types-data (3.2018.0812) - mimemagic (0.3.2) + mimemagic (0.3.3) mini_mime (1.0.1) - mini_portile2 (2.3.0) + mini_portile2 (2.4.0) minitest (5.11.3) moneta (1.0.0) multi_json (1.11.3) multi_xml (0.6.0) multipart-post (2.0.0) - newrelic_rpm (5.4.0.347) + newrelic_rpm (5.6.0.349) nio4r (2.3.1) - nokogiri (1.8.5) - mini_portile2 (~> 2.3.0) + nokogiri (1.9.1) + mini_portile2 (~> 2.4.0) oauth (0.5.4) - oauth2 (1.4.0) - faraday (>= 0.8, < 0.13) - jwt (~> 1.0) + oauth2 (1.4.1) + faraday (>= 0.8, < 0.16.0) + jwt (>= 1.0, < 3.0) multi_json (~> 1.3) multi_xml (~> 0.5) rack (>= 1.2, < 3) - omniauth (1.8.1) - hashie (>= 3.4.6, < 3.6.0) + omniauth (1.9.0) + hashie (>= 3.4.6, < 3.7.0) rack (>= 1.6.2, < 3) omniauth-facebook (5.0.0) omniauth-oauth2 (~> 1.2) @@ -318,9 +322,9 @@ GEM omniauth-oauth (1.1.0) oauth omniauth (~> 1.0) - omniauth-oauth2 (1.5.0) + omniauth-oauth2 (1.6.0) oauth2 (~> 1.1) - omniauth (~> 1.2) + omniauth (~> 1.9) omniauth-twitter (1.4.0) omniauth-oauth (~> 1.1) rack @@ -334,7 +338,7 @@ GEM parallel (1.12.1) paranoia (2.4.1) activerecord (>= 4.0, < 5.3) - parser (2.5.1.2) + parser (2.5.3.0) ast (~> 2.4.0) pg (0.21.0) platform-api (2.2.0) @@ -349,8 +353,8 @@ GEM powerpack (0.1.2) public_suffix (3.0.3) puma (3.12.0) - rack (2.0.5) - rack-protection (2.0.3) + rack (2.0.6) + rack-protection (2.0.5) rack rack-test (1.1.0) rack (>= 1.0, < 3) @@ -367,18 +371,18 @@ GEM railties (= 5.1.4) sprockets-rails (>= 2.0.0) rails-assets-leaflet (1.3.4) - rails-assets-leaflet.markercluster (1.4.0) + rails-assets-leaflet.markercluster (1.4.1) rails-assets-leaflet (>= 1.3.1) - rails-controller-testing (1.0.2) - actionpack (~> 5.x, >= 5.0.1) - actionview (~> 5.x, >= 5.0.1) - activesupport (~> 5.x) + rails-controller-testing (1.0.4) + actionpack (>= 5.0.1.x) + actionview (>= 5.0.1.x) + activesupport (>= 5.0.1.x) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) rails-html-sanitizer (1.0.4) loofah (~> 2.2, >= 2.2.2) - rails-i18n (5.1.1) + rails-i18n (5.1.2) i18n (>= 0.7, < 2) railties (>= 5.0, < 6) rails_12factor (0.0.3) @@ -394,15 +398,16 @@ GEM thor (>= 0.18.1, < 2.0) rainbow (3.0.0) raindrops (0.19.0) - rake (12.3.1) + rake (12.3.2) rb-fsevent (0.10.3) - rb-inotify (0.9.10) - ffi (>= 0.5.0, < 2) - redis (4.0.2) + rb-inotify (0.10.0) + ffi (~> 1.0) + redis (4.1.0) + regexp_parser (1.3.0) responders (2.4.0) actionpack (>= 4.2.0, < 5.3) railties (>= 4.2.0, < 5.3) - rspec-activemodel-mocks (1.0.3) + rspec-activemodel-mocks (1.1.0) activemodel (>= 3.0) activesupport (>= 3.0) rspec-mocks (>= 2.99, < 4.0) @@ -423,23 +428,23 @@ GEM rspec-mocks (~> 3.8.0) rspec-support (~> 3.8.0) rspec-support (3.8.0) - rubocop (0.59.0) + rubocop (0.61.1) jaro_winkler (~> 1.5.1) parallel (~> 1.10) parser (>= 2.5, != 2.5.1.1) powerpack (~> 0.1) rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) - unicode-display_width (~> 1.0, >= 1.0.1) - rubocop-rspec (1.29.1) - rubocop (>= 0.58.0) + unicode-display_width (~> 1.4.0) + rubocop-rspec (1.30.1) + rubocop (>= 0.60.0) ruby-progressbar (1.10.0) - ruby-units (2.3.0) + ruby-units (2.3.1) ruby_dep (1.5.0) - ruby_parser (3.11.0) + ruby_parser (3.12.0) sexp_processor (~> 4.9) rubyzip (1.2.2) - sass (3.5.7) + sass (3.7.2) sass-listen (~> 4.0.0) sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) @@ -450,14 +455,14 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - sassc (1.12.1) + sassc (2.0.0) ffi (~> 1.9.6) rake selenium-webdriver (3.141.0) childprocess (~> 0.5) rubyzip (~> 1.2, >= 1.2.2) sexp_processor (4.11.0) - sidekiq (5.2.1) + sidekiq (5.2.3) connection_pool (~> 2.2, >= 2.2.2) rack-protection (>= 1.5.0) redis (>= 3.3.5, < 5) @@ -477,7 +482,7 @@ GEM sprockets (>= 3.0.0) sysexits (1.2.0) temple (0.8.0) - term-ansicolor (1.6.0) + term-ansicolor (1.7.0) tins (~> 1.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) @@ -485,21 +490,21 @@ GEM climate_control (>= 0.0.3, < 1.0) thor (0.19.4) thread_safe (0.3.6) - tilt (2.0.8) + tilt (2.0.9) timecop (0.9.1) - tins (1.16.3) + tins (1.20.2) trollop (1.16.2) tzinfo (1.2.5) thread_safe (~> 0.1) - uglifier (4.1.19) + uglifier (4.1.20) execjs (>= 0.3.0, < 3) - unicode-display_width (1.4.0) + unicode-display_width (1.4.1) unicorn (5.4.1) kgio (~> 2.6) raindrops (~> 0.7) uniform_notifier (1.12.1) - warden (1.2.7) - rack (>= 1.0) + warden (1.2.8) + rack (>= 2.0.6) webrat (0.7.3) nokogiri (>= 1.2.0) rack (>= 1.0) From 27e471b7b3835e52a9e1c734cef3327c63d5fc1a Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 13:30:16 +1300 Subject: [PATCH 187/219] Set rubocop to 0.60 --- .codeclimate.yml | 1 + Gemfile | 2 +- Gemfile.lock | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.codeclimate.yml b/.codeclimate.yml index 32904c026..65de6c429 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,6 +1,7 @@ engines: rubocop: enabled: true + channel: rubocop-0-60 scss-lint: enabled: true shellcheck: diff --git a/Gemfile b/Gemfile index 31250e27a..7a6f3ff86 100644 --- a/Gemfile +++ b/Gemfile @@ -134,7 +134,7 @@ group :development, :test do gem 'poltergeist' # for headless JS testing gem 'rspec-activemodel-mocks' gem 'rspec-rails' # unit testing framework - gem 'rubocop', '>= 0.54.0' + gem 'rubocop', '~> 0.60' gem 'rubocop-rspec' gem 'selenium-webdriver' gem 'webrat' # provides HTML matchers for view tests diff --git a/Gemfile.lock b/Gemfile.lock index be2ea990f..8ebda85af 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -592,7 +592,7 @@ DEPENDENCIES responders rspec-activemodel-mocks rspec-rails - rubocop (>= 0.54.0) + rubocop (~> 0.60) rubocop-rspec ruby-units sass-rails From 2cc37464fc9bfe52f822cf9074086ff89e79fb0b Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 13:31:33 +1300 Subject: [PATCH 188/219] Some rubocop ignores that we shouldn't ignore --- .rubocop_todo.yml | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 1bfd8efc6..da3279a42 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -52,36 +52,12 @@ Rails/HasManyOrHasOneDependent: Exclude: - 'app/models/member.rb' -# Configuration parameters: Include. -# Include: app/controllers/**/*.rb -Rails/LexicallyScopedActionFilter: - Exclude: - - 'app/controllers/alternate_names_controller.rb' - - 'app/controllers/comments_controller.rb' - - 'app/controllers/gardens_controller.rb' - - 'app/controllers/members_controller.rb' - - 'app/controllers/photos_controller.rb' - -Rails/OutputSafety: - Exclude: - - 'app/helpers/application_helper.rb' - - 'app/helpers/auto_suggest_helper.rb' - - 'app/helpers/gardens_helper.rb' - # Configuration parameters: Blacklist. # Blacklist: decrement!, decrement_counter, increment!, increment_counter, toggle!, touch, update_all, update_attribute, update_column, update_columns, update_counters Rails/SkipsModelValidations: Exclude: - 'db/seeds.rb' -# Configuration parameters: EnforcedStyle. -# SupportedStyles: strict, flexible -Rails/TimeZone: - Exclude: - - 'spec/factories/member.rb' - - 'spec/factories/post.rb' - - 'spec/models/post_spec.rb' - # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, EnforcedStyle. # SupportedStyles: nested, compact @@ -97,10 +73,6 @@ Style/CommentedKeyword: - 'spec/models/photo_spec.rb' - 'spec/models/planting_spec.rb' -Style/IdenticalConditionalBranches: - Exclude: - - 'app/controllers/follows_controller.rb' - Style/MixinUsage: Exclude: - 'bin/setup' From 9e6f0af373fa76619eecd593e6e4430792bcc41a Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 13:33:34 +1300 Subject: [PATCH 189/219] More things detected by rubocop, that we shouldn't ignore --- .rubocop_todo.yml | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index da3279a42..eb05ad7ff 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -10,18 +10,6 @@ Lint/HandleExceptions: Exclude: - 'lib/tasks/testing.rake' -# Configuration parameters: MaximumRangeSize. -Lint/MissingCopEnableDirective: - Exclude: - - 'config.rb' - - 'config/compass.rb' - - 'config/initializers/devise.rb' - -Lint/UriEscapeUnescape: - Exclude: - - 'app/helpers/crops_helper.rb' - - 'spec/features/crops/crop_detail_page_spec.rb' - # Configuration parameters: EnforcedStyle. # SupportedStyles: lowercase, uppercase Naming/HeredocDelimiterCase: @@ -88,14 +76,3 @@ Style/NumericPredicate: - 'app/helpers/harvests_helper.rb' - 'app/helpers/plantings_helper.rb' - 'lib/tasks/growstuff.rake' - -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, AllowInnerSlashes. -# SupportedStyles: slashes, percent_r, mixed -Style/RegexpLiteral: - Exclude: - - 'spec/lib/haml/filters/growstuff_markdown_spec.rb' - - 'spec/rails_helper.rb' - - 'spec/views/devise/registrations/edit_spec.rb' - - 'spec/views/members/index.html.haml_spec.rb' - - 'spec/views/posts/index.html.haml_spec.rb' From 007e61e76a7d01c0b04f723722da7c5942129b87 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 13:36:32 +1300 Subject: [PATCH 190/219] Freeze mutable objects assigned to constants. --- lib/haml/filters/growstuff_markdown.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/haml/filters/growstuff_markdown.rb b/lib/haml/filters/growstuff_markdown.rb index 51d95d1fe..4e7617c15 100644 --- a/lib/haml/filters/growstuff_markdown.rb +++ b/lib/haml/filters/growstuff_markdown.rb @@ -13,10 +13,10 @@ module Haml::Filters # rubocop:disable Style/ClassAndModuleChildren private - CROP_REGEX = /(? Date: Sun, 30 Dec 2018 13:49:50 +1300 Subject: [PATCH 191/219] No longer need to create the active median function in a migration --- db/migrate/20171028230429_create_median_function.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20171028230429_create_median_function.rb b/db/migrate/20171028230429_create_median_function.rb index 5e57c01d1..77f8f76b6 100644 --- a/db/migrate/20171028230429_create_median_function.rb +++ b/db/migrate/20171028230429_create_median_function.rb @@ -1,6 +1,6 @@ class CreateMedianFunction < ActiveRecord::Migration[4.2] def up - ActiveMedian.create_function + # ActiveMedian.create_function end def down From c2b64ffbc0fbcc666de77fa91b4ee4bb13656751 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 14:19:33 +1300 Subject: [PATCH 192/219] pull elastic search version back --- Gemfile | 6 +++--- Gemfile.lock | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Gemfile b/Gemfile index 7a6f3ff86..1a61408bf 100644 --- a/Gemfile +++ b/Gemfile @@ -85,9 +85,9 @@ gem "chartkick" # Project does not use semver, so we want to be in sync with the version of # elasticsearch we use # See https://github.com/elastic/elasticsearch-ruby#compatibility -gem "elasticsearch-api", ">= 6.0.0" -gem "elasticsearch-model", ">= 6.0.0" -gem "elasticsearch-rails", ">= 6.0.0" +gem "elasticsearch-api", "~> 6.0.0" +gem "elasticsearch-model", "~> 6.0.0" +gem "elasticsearch-rails", "~> 6.0.0" gem "hashie", ">= 3.5.3" gem 'rake', '>= 10.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index 8ebda85af..118c4bcdd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -151,17 +151,17 @@ GEM warden (~> 1.2.3) diff-lcs (1.3) docile (1.1.5) - elasticsearch (6.1.0) - elasticsearch-api (= 6.1.0) - elasticsearch-transport (= 6.1.0) - elasticsearch-api (6.1.0) + elasticsearch (6.0.3) + elasticsearch-api (= 6.0.3) + elasticsearch-transport (= 6.0.3) + elasticsearch-api (6.0.3) multi_json elasticsearch-model (6.0.0) activesupport (> 3) elasticsearch (> 1) hashie elasticsearch-rails (6.0.0) - elasticsearch-transport (6.1.0) + elasticsearch-transport (6.0.3) faraday multi_json erubi (1.8.0) @@ -545,9 +545,9 @@ DEPENDENCIES dalli database_cleaner devise - elasticsearch-api (>= 6.0.0) - elasticsearch-model (>= 6.0.0) - elasticsearch-rails (>= 6.0.0) + elasticsearch-api (~> 6.0.0) + elasticsearch-model (~> 6.0.0) + elasticsearch-rails (~> 6.0.0) factory_bot_rails faker figaro From 1574f4b90af1af7db67024657d2d53f19947afb6 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 14:39:24 +1300 Subject: [PATCH 193/219] Downgrade active_median gem until we upgrade postgresql. see #1757 --- Gemfile | 3 ++- Gemfile.lock | 6 +++--- db/migrate/20171028230429_create_median_function.rb | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index 1a61408bf..310b741bf 100644 --- a/Gemfile +++ b/Gemfile @@ -22,7 +22,8 @@ gem 'font-awesome-sass' gem 'uglifier' # JavaScript compressor # planting and harvest predictions -gem 'active_median' +# based on median values for the crop +gem 'active_median', '0.1.4' # needs postgresql update https://github.com/Growstuff/growstuff/issues/1757 gem 'flickraw' gem 'jquery-rails' diff --git a/Gemfile.lock b/Gemfile.lock index 118c4bcdd..062eadd71 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -49,8 +49,8 @@ GEM active_link_to (1.0.5) actionpack addressable - active_median (0.2.2) - activerecord (>= 4.2) + active_median (0.1.4) + activerecord active_utils (3.3.16) activesupport (>= 4.2) i18n @@ -521,7 +521,7 @@ PLATFORMS ruby DEPENDENCIES - active_median + active_median (= 0.1.4) active_utils better_errors bluecloth diff --git a/db/migrate/20171028230429_create_median_function.rb b/db/migrate/20171028230429_create_median_function.rb index 77f8f76b6..5e57c01d1 100644 --- a/db/migrate/20171028230429_create_median_function.rb +++ b/db/migrate/20171028230429_create_median_function.rb @@ -1,6 +1,6 @@ class CreateMedianFunction < ActiveRecord::Migration[4.2] def up - # ActiveMedian.create_function + ActiveMedian.create_function end def down From 1e4cdc95116a1adc583e4f813fba3c719b1f888e Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 14:57:24 +1300 Subject: [PATCH 194/219] Change rake to rails --- CONTRIBUTING.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c8b53d513..2a6ab8b10 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,11 +10,11 @@ When you create a pull request, please include the following: All pull requests should pass our automatic continuous integration and style checks before being merged. You can run tests locally as follows: - - `rake spec` to run all Ruby tests - - `rake spec:models` to run Ruby model tests (or `rake spec:views` for view tests, etc) - - `rake static` to run all static checks (code style, unfixed Git conflicts, etc) - - `rake jasmine:ci` to run JavaScript unit tests in headless mode - - `rake jasmine` to start a server for running JavaScript unit tests in a + - `rails spec` to run all Ruby tests + - `rails spec:models` to run Ruby model tests (or `rails spec:views` for view tests, etc) + - `rails static` to run all static checks (code style, unfixed Git conflicts, etc) + - `rails jasmine:ci` to run JavaScript unit tests in headless mode + - `rails jasmine` to start a server for running JavaScript unit tests in a browser (eg for debugging). Point your browser at http://localhost:8888 to run the tests. - `rspec ./spec/path/to/my_spec.rb` to run all Ruby tests in the file `my_spec.rb` From b82b4fa64233f71884c742127b55daaf6b0dd17a Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:04:38 +1300 Subject: [PATCH 195/219] Comments controller has no show --- app/controllers/comments_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 389efb825..f6bbc412c 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -1,5 +1,5 @@ class CommentsController < ApplicationController - before_action :authenticate_member!, except: %i(index show) + before_action :authenticate_member!, except: %i(index) load_and_authorize_resource respond_to :html, :json respond_to :rss, only: :index From 24b58c5ac234dde0cbc468398ded40ee77c2d2df Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:04:50 +1300 Subject: [PATCH 196/219] FIXED wrong method name --- app/controllers/gardens_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/gardens_controller.rb b/app/controllers/gardens_controller.rb index 35efaf5d2..132430976 100644 --- a/app/controllers/gardens_controller.rb +++ b/app/controllers/gardens_controller.rb @@ -1,6 +1,6 @@ class GardensController < ApplicationController before_action :authenticate_member!, except: %i(index show) - after_action :expire_homepage, only: %i(create delete) + after_action :expire_homepage, only: %i(create destroy) load_and_authorize_resource respond_to :html, :json From 8e160a4c9380d339af6fd90220bbb605c346b1d0 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:05:00 +1300 Subject: [PATCH 197/219] Fixed another wrong method name --- app/controllers/photos_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb index 331a4e2c4..c74c89824 100644 --- a/app/controllers/photos_controller.rb +++ b/app/controllers/photos_controller.rb @@ -1,6 +1,6 @@ class PhotosController < ApplicationController before_action :authenticate_member!, except: %i(index show) - after_action :expire_homepage, only: %i(create delete) + after_action :expire_homepage, only: %i(create destroy) load_and_authorize_resource respond_to :html, :json responders :flash From 914e9ef3be633b12cc03c1d89878011722fbca64 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:05:10 +1300 Subject: [PATCH 198/219] photos needs a show method --- app/controllers/photos_controller.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb index c74c89824..576373207 100644 --- a/app/controllers/photos_controller.rb +++ b/app/controllers/photos_controller.rb @@ -5,6 +5,9 @@ class PhotosController < ApplicationController respond_to :html, :json responders :flash + def show + end + def index if params[:crop_id] @crop = Crop.find params[:crop_id] From ac6a308ea05574ece27f8077c06c174b82b11908 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:05:41 +1300 Subject: [PATCH 199/219] Update URL -> CGI --- spec/features/crops/crop_detail_page_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/crops/crop_detail_page_spec.rb b/spec/features/crops/crop_detail_page_spec.rb index 62f80d37f..a194e8f1e 100644 --- a/spec/features/crops/crop_detail_page_spec.rb +++ b/spec/features/crops/crop_detail_page_spec.rb @@ -141,12 +141,12 @@ feature "crop detail page", js: true do scenario "has a link to OpenFarm" do expect(page).to have_link "OpenFarm - Growing guide", - href: "https://openfarm.cc/en/crops/#{URI.escape crop.name}" + href: "https://openfarm.cc/en/crops/#{CGI.escape crop.name}" end scenario "has a link to gardenate" do expect(page).to have_link "Gardenate - Planting reminders", - href: "http://www.gardenate.com/plant/#{URI.escape crop.name}" + href: "http://www.gardenate.com/plant/#{CGI.escape crop.name}" end end end From 9f90b0368b39f2847275e000dd6069c71fa8bded Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:06:06 +1300 Subject: [PATCH 200/219] Use %r for regexp --- spec/lib/haml/filters/growstuff_markdown_spec.rb | 2 +- spec/rails_helper.rb | 2 +- spec/views/devise/registrations/edit_spec.rb | 2 +- spec/views/members/index.html.haml_spec.rb | 2 +- spec/views/posts/index.html.haml_spec.rb | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/lib/haml/filters/growstuff_markdown_spec.rb b/spec/lib/haml/filters/growstuff_markdown_spec.rb index e4dea52d9..597137f6d 100644 --- a/spec/lib/haml/filters/growstuff_markdown_spec.rb +++ b/spec/lib/haml/filters/growstuff_markdown_spec.rb @@ -58,7 +58,7 @@ describe 'Haml::Filters::Growstuff_Markdown' do it "converts normal markdown" do string = "**foo**" rendered = Haml::Filters::GrowstuffMarkdown.render(string) - expect(rendered).to match(/foo<\/strong>/) + expect(rendered).to match(%r{foo}) end it "finds crops case insensitively" do diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index ec3f6af77..5b89cd26d 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -37,7 +37,7 @@ if ENV['GROWSTUFF_CAPYBARA_DRIVER'].present? end Capybara::Screenshot.register_filename_prefix_formatter(:rspec) do |example| - "screenshot_#{example.description.tr(' ', '-').gsub(/^.*\/spec\//, '')}" + "screenshot_#{example.description.tr(' ', '-').gsub(%r{^.*/spec/}, '')}" end Capybara.app_host = 'http://localhost' diff --git a/spec/views/devise/registrations/edit_spec.rb b/spec/views/devise/registrations/edit_spec.rb index 64af90017..85ee6588a 100644 --- a/spec/views/devise/registrations/edit_spec.rb +++ b/spec/views/devise/registrations/edit_spec.rb @@ -41,7 +41,7 @@ describe 'devise/registrations/edit.html.haml', type: "view" do end it "contains a gravatar icon" do - assert_select "img", src: /gravatar\.com\/avatar/ + assert_select "img", src: %r{gravatar\.com/avatar} end it 'contains a link to gravatar.com' do diff --git a/spec/views/members/index.html.haml_spec.rb b/spec/views/members/index.html.haml_spec.rb index 87dd3a285..9488fa5df 100644 --- a/spec/views/members/index.html.haml_spec.rb +++ b/spec/views/members/index.html.haml_spec.rb @@ -16,7 +16,7 @@ describe "members/index" do end it "contains two gravatar icons" do - assert_select "img", src: /gravatar\.com\/avatar/, count: 2 + assert_select "img", src: %r{gravatar\.com/avatar}, count: 2 end it 'contains member locations' do diff --git a/spec/views/posts/index.html.haml_spec.rb b/spec/views/posts/index.html.haml_spec.rb index eeebac8b6..b2f956904 100644 --- a/spec/views/posts/index.html.haml_spec.rb +++ b/spec/views/posts/index.html.haml_spec.rb @@ -25,7 +25,7 @@ describe "posts/index" do end it "contains two gravatar icons" do - assert_select "img", src: /gravatar\.com\/avatar/, count: 2 + assert_select "img", src: %r{gravatar\.com/avatar}, count: 2 end it "contains RSS feed links for posts and comments" do From d5676338d685f99521e304e6c6553dddefb55352 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:06:14 +1300 Subject: [PATCH 201/219] Use the expect syntax --- spec/controllers/comments_controller_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/controllers/comments_controller_spec.rb b/spec/controllers/comments_controller_spec.rb index f61c28d81..4d9e9fc6b 100644 --- a/spec/controllers/comments_controller_spec.rb +++ b/spec/controllers/comments_controller_spec.rb @@ -36,13 +36,13 @@ describe CommentsController do before { get :new, params: { post_id: post.id } } it "picks up post from params" do - assigns(:post).should eq(post) + expect(assigns(:post)).to eq(post) end let(:old_comment) { FactoryBot.create(:comment, post: post) } it "assigns the old comments as @comments" do - assigns(:comments).should eq [old_comment] + expect(assigns(:comments)).to eq [old_comment] end end From 22e456f3af124440a4387ad307e54bbacbea14b0 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:06:24 +1300 Subject: [PATCH 202/219] Time stamps need a timezone --- spec/models/post_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index 7ae50f7f2..6c2d9b32e 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -60,7 +60,7 @@ describe Post do context "recent activity" do before do - Time.stub(now: Time.now) + Time.stub(now: Time.zone.now) end let!(:post) { FactoryBot.create(:post, created_at: 1.day.ago) } From 3fec343f2c16d39c1fc529d760fca4c79fd8a6b1 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:13:59 +1300 Subject: [PATCH 203/219] Rubocop for hash alignment --- .rubocop.yml | 4 ++++ config/environments/development.rb | 8 ++++---- config/environments/test.rb | 26 +++++++++++++------------- config/initializers/geocoder.rb | 8 ++++---- config/routes.rb | 6 +++--- 5 files changed, 28 insertions(+), 24 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 316ffcde7..7c61c6fd1 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -26,6 +26,10 @@ Style/PercentLiteralDelimiters: Layout/MultilineMethodCallIndentation: EnforcedStyle: indented +Layout/AlignHash: + EnforcedColonStyle: table + EnforcedHashRocketStyle: table + # Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. # SupportedStyles: with_first_parameter, with_fixed_indentation Layout/AlignParameters: diff --git a/config/environments/development.rb b/config/environments/development.rb index 363c0e32a..7b04cf96c 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -57,10 +57,10 @@ Rails.application.configure do config.action_mailer.delivery_method = :letter_opener config.action_mailer.smtp_settings = { - port: '587', - address: 'smtp.mandrillapp.com', - user_name: ENV['GROWSTUFF_MANDRILL_USERNAME'], - password: ENV['GROWSTUFF_MANDRILL_APIKEY'], + port: '587', + address: 'smtp.mandrillapp.com', + user_name: ENV['GROWSTUFF_MANDRILL_USERNAME'], + password: ENV['GROWSTUFF_MANDRILL_APIKEY'], authentication: :login } diff --git a/config/environments/test.rb b/config/environments/test.rb index cbf6101f0..47fede3a8 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -55,8 +55,8 @@ Geocoder.configure(lookup: :test) Geocoder::Lookup::Test.add_stub( "Amundsen-Scott Base, Antarctica", [ { - 'latitude' => -90.0, - 'longitude' => 0.0 + 'latitude' => -90.0, + 'longitude' => 0.0 } ] ) @@ -78,8 +78,8 @@ Geocoder::Lookup::Test.add_stub( Geocoder::Lookup::Test.add_stub( "Greenwich, UK", [ { - 'latitude' => 51.483061, - 'longitude' => -0.004151 + 'latitude' => 51.483061, + 'longitude' => -0.004151 } ] ) @@ -87,8 +87,8 @@ Geocoder::Lookup::Test.add_stub( Geocoder::Lookup::Test.add_stub( "Edinburgh", [ { - 'latitude' => 55.953252, - 'longitude' => -3.188267 + 'latitude' => 55.953252, + 'longitude' => -3.188267 } ] ) @@ -103,15 +103,15 @@ end OmniAuth.config.test_mode = true # Fake the omniauth -OmniAuth.config.mock_auth[:facebook] = OmniAuth::AuthHash.new(provider: 'facebook', - uid: '123545', - info: { - name: "John Testerson", +OmniAuth.config.mock_auth[:facebook] = OmniAuth::AuthHash.new(provider: 'facebook', + uid: '123545', + info: { + name: "John Testerson", nickname: 'JohnnyT', - email: 'example.oauth.facebook@example.com', - image: 'http://findicons.com/files/icons/1072/face_avatars/300/i04.png' + email: 'example.oauth.facebook@example.com', + image: 'http://findicons.com/files/icons/1072/face_avatars/300/i04.png' }, credentials: { - token: "token", + token: "token", secret: "donttell" }) diff --git a/config/initializers/geocoder.rb b/config/initializers/geocoder.rb index b31f1edf3..7b0b683bb 100644 --- a/config/initializers/geocoder.rb +++ b/config/initializers/geocoder.rb @@ -1,12 +1,12 @@ require 'geocodable' Geocoder.configure( - units: :km, - timeout: 10, + units: :km, + timeout: 10, http_headers: { "User-Agent" => - "#{Rails.application.config.user_agent} #{Rails.application.config.user_agent_email}", - "From" => Rails.application.config.user_agent_email + "#{Rails.application.config.user_agent} #{Rails.application.config.user_agent_email}", + "From" => Rails.application.config.user_agent_email } ) # This configuration takes precedence over environment/test.rb diff --git a/config/routes.rb b/config/routes.rb index a43de3f0c..9f8918eed 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,9 +4,9 @@ Rails.application.routes.draw do resources :plant_parts devise_for :members, controllers: { - registrations: "registrations", - passwords: "passwords", - sessions: "sessions", + registrations: "registrations", + passwords: "passwords", + sessions: "sessions", omniauth_callbacks: "omniauth_callbacks" } devise_scope :member do From 0330809e4b2f54a023a8d217a51ed3ff64b44393 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:16:08 +1300 Subject: [PATCH 204/219] Re-enable rubocop cops --- config/compass.rb | 1 + config/initializers/devise.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/config/compass.rb b/config/compass.rb index 306cdd02c..028c02792 100644 --- a/config/compass.rb +++ b/config/compass.rb @@ -1,3 +1,4 @@ # Require any additional compass plugins here. # rubocop:disable Lint/UselessAssignment project_type = :rails +# rubocop:enable Lint/UselessAssignment diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index ec28eb983..fcb4caecc 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -236,3 +236,4 @@ Devise.setup do |config| # Later we may wish to ask for user_photos,user_location, however this means we need to be reviewed by facebook config.omniauth :facebook, ENV['GROWSTUFF_FACEBOOK_KEY'], ENV['GROWSTUFF_FACEBOOK_SECRET'], scope: 'email,public_profile', display: 'page', info_fields: 'email,name,first_name,last_name,id' end +# rubocop:enable Metrics/LineLength From 7399346354903dc14c29549ecc33428e7651fa40 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:18:11 +1300 Subject: [PATCH 205/219] one line method --- app/controllers/photos_controller.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb index 576373207..5a9f0129b 100644 --- a/app/controllers/photos_controller.rb +++ b/app/controllers/photos_controller.rb @@ -5,8 +5,7 @@ class PhotosController < ApplicationController respond_to :html, :json responders :flash - def show - end + def show; end def index if params[:crop_id] From 7b01fcbd4b73745fa859d862b1e53bfc206171de Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:22:29 +1300 Subject: [PATCH 206/219] Align all the hashes --- Guardfile | 2 +- app/controllers/authentications_controller.rb | 8 +-- app/controllers/crops_controller.rb | 4 +- app/controllers/likes_controller.rb | 8 +-- app/controllers/members_controller.rb | 2 +- app/helpers/application_helper.rb | 2 +- app/mailers/notifier.rb | 2 +- app/models/comment.rb | 8 +-- app/models/crop.rb | 20 +++--- app/models/csv_importer.rb | 2 +- app/models/follow.rb | 6 +- app/models/garden.rb | 16 ++--- app/models/harvest.rb | 18 +++--- app/models/member.rb | 22 +++---- app/models/photo.rb | 16 ++--- app/models/planting.rb | 12 ++-- app/models/post.rb | 6 +- app/models/seed.rb | 6 +- app/services/crop_search_service.rb | 2 +- db/migrate/20150201052245_create_cms.rb | 4 +- db/migrate/20180213005731_seed_usage.rb | 12 ++-- db/seeds.rb | 34 +++++----- lib/actions/oauth_signup_action.rb | 10 +-- lib/tasks/growstuff.rake | 2 +- .../authentications_controller_spec.rb | 6 +- spec/controllers/crops_controller_spec.rb | 4 +- spec/controllers/forums_controller_spec.rb | 4 +- spec/controllers/harvests_controller_spec.rb | 8 +-- .../notifications_controller_spec.rb | 8 +-- .../photo_associations_controller_spec.rb | 4 +- spec/controllers/photos_controller_spec.rb | 22 +++---- spec/controllers/plantings_controller_spec.rb | 2 +- spec/features/crops/crop_detail_page_spec.rb | 4 +- spec/features/crops/crop_photos_spec.rb | 24 +++---- spec/features/gardens/gardens_index_spec.rb | 14 ++-- spec/features/notifications_spec.rb | 6 +- spec/helpers/harvests_helper_spec.rb | 32 +++++----- spec/helpers/plantings_helper_spec.rb | 24 +++---- spec/lib/actions/oauth_signup_action_spec.rb | 28 ++++---- spec/models/ability_spec.rb | 4 +- spec/models/alternate_name_spec.rb | 4 +- spec/models/crop_spec.rb | 22 +++---- spec/models/harvest_spec.rb | 64 +++++++++---------- spec/models/plant_part_spec.rb | 8 +-- spec/models/planting_spec.rb | 8 +-- spec/models/post_spec.rb | 2 +- spec/requests/api/v1/crop_request_spec.rb | 42 ++++++------ spec/requests/api/v1/gardens_request_spec.rb | 32 +++++----- spec/requests/api/v1/harvest_request_spec.rb | 44 ++++++------- spec/requests/api/v1/member_request_spec.rb | 40 ++++++------ spec/requests/api/v1/photos_request_spec.rb | 40 ++++++------ .../requests/api/v1/plantings_request_spec.rb | 60 ++++++++--------- spec/requests/api/v1/seeds_request_spec.rb | 32 +++++----- spec/views/crops/_grown_for.html.haml_spec.rb | 2 +- spec/views/forums/edit.html.haml_spec.rb | 4 +- spec/views/harvests/index.html.haml_spec.rb | 6 +- spec/views/harvests/index.rss.haml_spec.rb | 8 +-- .../notifications/index.html.haml_spec.rb | 2 +- spec/views/photos/edit.html.haml_spec.rb | 6 +- spec/views/plantings/_form.html.haml_spec.rb | 6 +- spec/views/plantings/edit.html.haml_spec.rb | 2 +- spec/views/plantings/index.html.haml_spec.rb | 22 +++---- spec/views/plantings/new.html.haml_spec.rb | 4 +- spec/views/posts/edit.html.haml_spec.rb | 2 +- spec/views/roles/edit.html.haml_spec.rb | 2 +- spec/views/roles/index.html.haml_spec.rb | 4 +- spec/views/roles/new.html.haml_spec.rb | 2 +- spec/views/roles/show.html.haml_spec.rb | 2 +- 68 files changed, 445 insertions(+), 445 deletions(-) diff --git a/Guardfile b/Guardfile index 4b5f3ccb5..0d6759dbf 100644 --- a/Guardfile +++ b/Guardfile @@ -1,5 +1,5 @@ guard :rspec, - cmd: 'bundle exec rspec --format documentation', + cmd: 'bundle exec rspec --format documentation', failed_mode: :keep do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/libs/#{m[1]}_spec.rb" } diff --git a/app/controllers/authentications_controller.rb b/app/controllers/authentications_controller.rb index fc18ff4d7..f6bf1bb21 100644 --- a/app/controllers/authentications_controller.rb +++ b/app/controllers/authentications_controller.rb @@ -12,14 +12,14 @@ class AuthenticationsController < ApplicationController @authentication = current_member.authentications .create_with( - name: name, - token: auth['credentials']['token'], + name: name, + token: auth['credentials']['token'], secret: auth['credentials']['secret'] ) .find_or_create_by( provider: auth['provider'], - uid: auth['uid'], - name: name + uid: auth['uid'], + name: name ) flash[:notice] = "Authentication successful." diff --git a/app/controllers/crops_controller.rb b/app/controllers/crops_controller.rb index c5b268f8b..abfb47a0e 100644 --- a/app/controllers/crops_controller.rb +++ b/app/controllers/crops_controller.rb @@ -173,13 +173,13 @@ class CropsController < ApplicationController def crop_json_fields { include: { - plantings: { + plantings: { include: { owner: { only: %i(id login_name location latitude longitude) } } }, scientific_names: { only: [:name] }, - alternate_names: { only: [:name] } + alternate_names: { only: [:name] } } } end diff --git a/app/controllers/likes_controller.rb b/app/controllers/likes_controller.rb index 0fdc2f407..e942d5b1f 100644 --- a/app/controllers/likes_controller.rb +++ b/app/controllers/likes_controller.rb @@ -24,10 +24,10 @@ class LikesController < ApplicationController def render_json(like, liked_by_member: true) { - id: like.likeable.id, + id: like.likeable.id, liked_by_member: liked_by_member, - description: ActionController::Base.helpers.pluralize(like.likeable.likes.count, "like"), - url: like_path(like, format: :json) + description: ActionController::Base.helpers.pluralize(like.likeable.likes.count, "like"), + url: like_path(like, format: :json) } end @@ -35,7 +35,7 @@ class LikesController < ApplicationController respond_to do |format| format.html { redirect_to like.likeable } format.json do - render(json: render_json(like, liked_by_member: liked_by_member), + render(json: render_json(like, liked_by_member: liked_by_member), status: status_code) end end diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb index ae720e57b..9576d6750 100644 --- a/app/controllers/members_controller.rb +++ b/app/controllers/members_controller.rb @@ -51,7 +51,7 @@ class MembersController < ApplicationController EMAIL_TYPE_STRING = { send_notification_email: "direct message notifications", - send_planting_reminder: "planting reminders" + send_planting_reminder: "planting reminders" }.freeze def unsubscribe diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 97ff16b76..9e65fc303 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -53,7 +53,7 @@ module ApplicationHelper return uri.to_s end - Gravatar.new(member.email).image_url(size: size, + Gravatar.new(member.email).image_url(size: size, default: :identicon) end diff --git a/app/mailers/notifier.rb b/app/mailers/notifier.rb index 6b7420cba..0d4ea407e 100644 --- a/app/mailers/notifier.rb +++ b/app/mailers/notifier.rb @@ -19,7 +19,7 @@ class Notifier < ApplicationMailer message = { member_id: @notification.recipient.id, type: :send_notification_email } @signed_message = verifier.generate(message) - mail(to: @notification.recipient.email, + mail(to: @notification.recipient.email, subject: @notification.subject) end diff --git a/app/models/comment.rb b/app/models/comment.rb index 3c9f3ce42..182e1e61f 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -11,10 +11,10 @@ class Comment < ApplicationRecord if recipient != sender Notification.create( recipient_id: recipient, - sender_id: sender, - subject: "#{author} commented on #{post.subject}", - body: body, - post_id: post.id + sender_id: sender, + subject: "#{author} commented on #{post.subject}", + body: body, + post_id: post.id ) end end diff --git a/app/models/crop.rb b/app/models/crop.rb index 525e3fb8d..2846b8a5f 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -44,10 +44,10 @@ class Crop < ApplicationRecord ## Wikipedia urls are only necessary when approving a crop validates :en_wikipedia_url, format: { - with: %r{\Ahttps?:\/\/en\.wikipedia\.org\/wiki\/[[:alnum:]%_\.()-]+\z}, + with: %r{\Ahttps?:\/\/en\.wikipedia\.org\/wiki\/[[:alnum:]%_\.()-]+\z}, message: 'is not a valid English Wikipedia URL' }, - if: :approved? + if: :approved? #################################### # Elastic search configuration @@ -57,22 +57,22 @@ class Crop < ApplicationRecord # In order to avoid clashing between different environments, # use Rails.env as a part of index name (eg. development_growstuff) index_name [Rails.env, "growstuff"].join('_') - settings index: { number_of_shards: 1 }, + settings index: { number_of_shards: 1 }, analysis: { tokenizer: { gs_edgeNGram_tokenizer: { - type: "edgeNGram", # edgeNGram: NGram match from the start of a token - min_gram: 3, - max_gram: 10, + type: "edgeNGram", # edgeNGram: NGram match from the start of a token + min_gram: 3, + max_gram: 10, # token_chars: Elasticsearch will split on characters # that don't belong to any of these classes token_chars: %w(letter digit) } }, - analyzer: { + analyzer: { gs_edgeNGram_analyzer: { tokenizer: "gs_edgeNGram_tokenizer", - filter: ["lowercase"] + filter: ["lowercase"] } } } do @@ -82,11 +82,11 @@ class Crop < ApplicationRecord indexes :approval_status, type: 'text' indexes :scientific_names do indexes :name, - type: 'text', + type: 'text', analyzer: 'gs_edgeNGram_analyzer', # Disabling field-length norm (norm). If the norm option is turned on(by default), # higher weigh would be given for shorter fields, which in our case is irrelevant. - norms: { enabled: false } + norms: { enabled: false } end indexes :alternate_names do indexes :name, type: 'text', analyzer: 'gs_edgeNGram_analyzer' diff --git a/app/models/csv_importer.rb b/app/models/csv_importer.rb index d3b992aba..d228bb3e1 100644 --- a/app/models/csv_importer.rb +++ b/app/models/csv_importer.rb @@ -11,7 +11,7 @@ class CsvImporter @crop = Crop.find_or_create_by(name: name) @crop.update( en_wikipedia_url: en_wikipedia_url, - creator_id: cropbot.id + creator_id: cropbot.id ) add_parent(parent_name) if parent_name diff --git a/app/models/follow.rb b/app/models/follow.rb index a70fd15f6..6562ca92c 100644 --- a/app/models/follow.rb +++ b/app/models/follow.rb @@ -6,9 +6,9 @@ class Follow < ApplicationRecord after_create do Notification.create( recipient_id: followed_id, - sender_id: follower_id, - subject: "#{follower.login_name} is now following you", - body: "#{follower.login_name} just followed you on #{ENV['GROWSTUFF_SITE_NAME']}. " + sender_id: follower_id, + subject: "#{follower.login_name} is now following you", + body: "#{follower.login_name} just followed you on #{ENV['GROWSTUFF_SITE_NAME']}. " ) end end diff --git a/app/models/garden.rb b/app/models/garden.rb index 01466120f..880dda91e 100644 --- a/app/models/garden.rb +++ b/app/models/garden.rb @@ -29,20 +29,20 @@ class Garden < ApplicationRecord validates :area, numericality: { - only_integer: false, + only_integer: false, greater_than_or_equal_to: 0 }, - allow_nil: true + allow_nil: true AREA_UNITS_VALUES = { "square metres" => "square metre", - "square feet" => "square foot", - "hectares" => "hectare", - "acres" => "acre" + "square feet" => "square foot", + "hectares" => "hectare", + "acres" => "acre" }.freeze - validates :area_unit, inclusion: { in: AREA_UNITS_VALUES.values, - message: "%s is not a valid area unit" }, - allow_nil: true, + validates :area_unit, inclusion: { in: AREA_UNITS_VALUES.values, + message: "%s is not a valid area unit" }, + allow_nil: true, allow_blank: true after_validation :cleanup_area diff --git a/app/models/harvest.rb b/app/models/harvest.rb index 987c83d0b..94b71d7b4 100644 --- a/app/models/harvest.rb +++ b/app/models/harvest.rb @@ -9,15 +9,15 @@ class Harvest < ApplicationRecord # Constants UNITS_VALUES = { "individual" => "individual", - "bunches" => "bunch", - "sprigs" => "sprig", - "handfuls" => "handful", - "litres" => "litre", - "pints" => "pint", - "quarts" => "quart", - "buckets" => "bucket", - "baskets" => "basket", - "bushels" => "bushel" + "bunches" => "bunch", + "sprigs" => "sprig", + "handfuls" => "handful", + "litres" => "litre", + "pints" => "pint", + "quarts" => "quart", + "buckets" => "bucket", + "baskets" => "basket", + "bushels" => "bushel" }.freeze WEIGHT_UNITS_VALUES = { diff --git a/app/models/member.rb b/app/models/member.rb index 3a997840d..2247bd038 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -69,13 +69,13 @@ class Member < ApplicationRecord # Requires acceptance of the Terms of Service validates :tos_agreement, acceptance: { allow_nil: true, accept: true } validates :login_name, - length: { + length: { minimum: 2, maximum: 25, message: "should be between 2 and 25 characters long" }, - exclusion: { + exclusion: { in: %w(growstuff admin moderator staff nearby), message: "name is reserved" }, - format: { + format: { with: /\A\w+\z/, message: "may only include letters, numbers, or underscores" }, uniqueness: { @@ -136,13 +136,13 @@ class Member < ApplicationRecord result = if set flickr.photosets.getPhotos( photoset_id: set, - page: page_num, - per_page: 30 + page: page_num, + per_page: 30 ) else flickr.people.getPhotos( - user_id: 'me', - page: page_num, + user_id: 'me', + page: page_num, per_page: 30 ) end @@ -199,9 +199,9 @@ class Member < ApplicationRecord return true if Rails.env.test? && !testing gibbon.lists.subscribe( - id: Rails.application.config.newsletter_list_id, - email: { email: email }, - merge_vars: { login_name: login_name }, + id: Rails.application.config.newsletter_list_id, + email: { email: email }, + merge_vars: { login_name: login_name }, double_optin: false # they already confirmed their email with us ) end @@ -209,7 +209,7 @@ class Member < ApplicationRecord def newsletter_unsubscribe(gibbon = Gibbon::API.new, testing = false) return true if Rails.env.test? && !testing - gibbon.lists.unsubscribe(id: Rails.application.config.newsletter_list_id, + gibbon.lists.unsubscribe(id: Rails.application.config.newsletter_list_id, email: { email: email }) end diff --git a/app/models/photo.rb b/app/models/photo.rb index f3636760c..87e9f9f4c 100644 --- a/app/models/photo.rb +++ b/app/models/photo.rb @@ -8,8 +8,8 @@ class Photo < ApplicationRecord # creates a relationship for each assignee type PHOTO_CAPABLE.each do |type| has_many type.downcase.pluralize.to_s.to_sym, - through: :photographings, - source: :photographable, + through: :photographings, + source: :photographable, source_type: type end @@ -23,13 +23,13 @@ class Photo < ApplicationRecord licenses = flickr.photos.licenses.getInfo license = licenses.find { |l| l.id == info.license } { - title: calculate_title(info), - license_name: license.name, - license_url: license.url, + title: calculate_title(info), + license_name: license.name, + license_url: license.url, thumbnail_url: FlickRaw.url_q(info), - fullsize_url: FlickRaw.url_z(info), - link_url: FlickRaw.url_photopage(info), - date_taken: info.dates.taken + fullsize_url: FlickRaw.url_z(info), + link_url: FlickRaw.url_photopage(info), + date_taken: info.dates.taken } end diff --git a/app/models/planting.rb b/app/models/planting.rb index 27f99870b..984b55c03 100644 --- a/app/models/planting.rb +++ b/app/models/planting.rb @@ -21,14 +21,14 @@ class Planting < ApplicationRecord # # Ancestry of food - belongs_to :parent_seed, class_name: 'Seed', # parent + belongs_to :parent_seed, class_name: 'Seed', # parent foreign_key: 'parent_seed_id', - required: false, - inverse_of: :child_plantings - has_many :child_seeds, class_name: 'Seed', # children + required: false, + inverse_of: :child_plantings + has_many :child_seeds, class_name: 'Seed', # children foreign_key: 'parent_planting_id', - inverse_of: :parent_planting, - dependent: :nullify + inverse_of: :parent_planting, + dependent: :nullify ## ## Scopes diff --git a/app/models/post.rb b/app/models/post.rb index 9e570b845..46efa2e45 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -80,9 +80,9 @@ class Post < ApplicationRecord Notification.create( recipient_id: recipient_id, - sender_id: sender, - subject: "#{author} mentioned you in their post #{subject}", - body: body + sender_id: sender, + subject: "#{author} mentioned you in their post #{subject}", + body: body ) end end diff --git a/app/models/seed.rb b/app/models/seed.rb index d9a2261d2..2075afba2 100644 --- a/app/models/seed.rb +++ b/app/models/seed.rb @@ -23,11 +23,11 @@ class Seed < ApplicationRecord # Validations validates :crop, approved: true validates :crop, presence: { message: "must be present and exist in our database" } - validates :quantity, allow_nil: true, + validates :quantity, allow_nil: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 } - validates :days_until_maturity_min, allow_nil: true, + validates :days_until_maturity_min, allow_nil: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 } - validates :days_until_maturity_max, allow_nil: true, + validates :days_until_maturity_max, allow_nil: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 } validates :tradable_to, allow_nil: false, allow_blank: false, inclusion: { in: TRADABLE_TO_VALUES, message: "You may only trade seed nowhere, "\ diff --git a/app/services/crop_search_service.rb b/app/services/crop_search_service.rb index fb7716019..8fce60b2d 100644 --- a/app/services/crop_search_service.rb +++ b/app/services/crop_search_service.rb @@ -9,7 +9,7 @@ class CropSearchService filter: { term: { "approval_status" => "approved" } }, - must: { + must: { query_string: { query: "*#{search_str}*" } diff --git a/db/migrate/20150201052245_create_cms.rb b/db/migrate/20150201052245_create_cms.rb index 91fea54e8..71ba20b11 100644 --- a/db/migrate/20150201052245_create_cms.rb +++ b/db/migrate/20150201052245_create_cms.rb @@ -113,7 +113,7 @@ class CreateCms < ActiveRecord::Migration[4.2] end add_index :comfy_cms_categories, %i(site_id categorized_type label), unique: true, - name: 'index_cms_categories_on_site_id_and_cat_type_and_label' + name: 'index_cms_categories_on_site_id_and_cat_type_and_label' create_table :comfy_cms_categorizations, force: true do |t| t.integer :category_id, null: false @@ -122,7 +122,7 @@ class CreateCms < ActiveRecord::Migration[4.2] end add_index :comfy_cms_categorizations, %i(category_id categorized_type categorized_id), unique: true, - name: 'index_cms_categorizations_on_cat_id_and_catd_type_and_catd_id' + name: 'index_cms_categorizations_on_cat_id_and_catd_type_and_catd_id' end def self.down diff --git a/db/migrate/20180213005731_seed_usage.rb b/db/migrate/20180213005731_seed_usage.rb index 0a94cb71a..0e1332ab3 100644 --- a/db/migrate/20180213005731_seed_usage.rb +++ b/db/migrate/20180213005731_seed_usage.rb @@ -7,16 +7,16 @@ class SeedUsage < ActiveRecord::Migration[4.2] # plantings can be grown from a seed add_column(:plantings, :parent_seed_id, :integer) add_foreign_key(:plantings, :seeds, - column: :parent_seed_id, + column: :parent_seed_id, primary_key: :id, - name: :parent_seed, - on_delete: :nullify) + name: :parent_seed, + on_delete: :nullify) # seeds can be harvest from planting add_column(:seeds, :parent_planting_id, :integer) add_foreign_key(:seeds, :plantings, - column: :parent_planting_id, + column: :parent_planting_id, primary_key: :id, - name: :parent_planting, - on_delete: :nullify) + name: :parent_planting, + on_delete: :nullify) end end diff --git a/db/seeds.rb b/db/seeds.rb index 4781bc80f..378b98875 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -57,9 +57,9 @@ def load_test_users # rubocop:disable Metrics/AbcSize (1..member_size).each do |i| @user = Member.new( - login_name: "test#{i}", - email: "test#{i}@example.com", - password: "password#{i}", + login_name: "test#{i}", + email: "test#{i}@example.com", + password: "password#{i}", tos_agreement: true ) @user.skip_confirmation! @@ -79,11 +79,11 @@ def load_test_users # rubocop:disable Metrics/AbcSize # Create a planting by the member Planting.create( - owner_id: @user.id, - garden_id: @user.gardens.first.id, - planted_at: Time.zone.today, - crop_id: Crop.find(i % Crop.all.size + 1).id, - sunniness: select_random_item(Planting::SUNNINESS_VALUES), + owner_id: @user.id, + garden_id: @user.gardens.first.id, + planted_at: Time.zone.today, + crop_id: Crop.find(i % Crop.all.size + 1).id, + sunniness: select_random_item(Planting::SUNNINESS_VALUES), planted_from: select_random_item(Planting::PLANTED_FROM_VALUES) ) end @@ -94,9 +94,9 @@ end def load_admin_users puts "Adding admin and crop wrangler members..." @admin_user = Member.new( - login_name: "admin1", - email: "admin1@example.com", - password: "password1", + login_name: "admin1", + email: "admin1@example.com", + password: "password1", tos_agreement: true ) @admin_user.skip_confirmation! @@ -104,9 +104,9 @@ def load_admin_users @admin_user.save! @wrangler_user = Member.new( - login_name: "wrangler1", - email: "wrangler1@example.com", - password: "password1", + login_name: "wrangler1", + email: "wrangler1@example.com", + password: "password1", tos_agreement: true ) @wrangler_user.skip_confirmation! @@ -116,9 +116,9 @@ end def create_cropbot @cropbot_user = Member.new( - login_name: "cropbot", - email: Rails.application.config.bot_email, - password: SecureRandom.urlsafe_base64(64), + login_name: "cropbot", + email: Rails.application.config.bot_email, + password: SecureRandom.urlsafe_base64(64), tos_agreement: true ) @cropbot_user.skip_confirmation! diff --git a/lib/actions/oauth_signup_action.rb b/lib/actions/oauth_signup_action.rb index de274dc35..3d80cee25 100644 --- a/lib/actions/oauth_signup_action.rb +++ b/lib/actions/oauth_signup_action.rb @@ -41,14 +41,14 @@ class Growstuff::OauthSignupAction authentication = member.authentications .create_with( - name: name, - token: auth['credentials']['token'], + name: name, + token: auth['credentials']['token'], secret: auth['credentials']['secret'] ) .find_or_create_by( - provider: auth['provider'], - uid: auth['uid'], - name: name, + provider: auth['provider'], + uid: auth['uid'], + name: name, member_id: member.id ) diff --git a/lib/tasks/growstuff.rake b/lib/tasks/growstuff.rake index 4a6234c4b..4f22214ea 100644 --- a/lib/tasks/growstuff.rake +++ b/lib/tasks/growstuff.rake @@ -225,7 +225,7 @@ namespace :growstuff do if crop alternate_names.split(/,\s*/).each do |an| AlternateName.where( - name: an, + name: an, crop_id: crop.id ).first_or_create do |x| x.creator = cropbot diff --git a/spec/controllers/authentications_controller_spec.rb b/spec/controllers/authentications_controller_spec.rb index 8b61bbddc..db4bec89a 100644 --- a/spec/controllers/authentications_controller_spec.rb +++ b/spec/controllers/authentications_controller_spec.rb @@ -7,9 +7,9 @@ describe AuthenticationsController do controller.stub(:current_member) { @member } @auth = FactoryBot.create(:authentication, member: @member) request.env['omniauth.auth'] = { - 'provider' => 'foo', - 'uid' => 'bar', - 'info' => { 'nickname' => 'blah' }, + 'provider' => 'foo', + 'uid' => 'bar', + 'info' => { 'nickname' => 'blah' }, 'credentials' => { 'token' => 'blah', 'secret' => 'blah' } } end diff --git a/spec/controllers/crops_controller_spec.rb b/spec/controllers/crops_controller_spec.rb index afdea715c..ad011da74 100644 --- a/spec/controllers/crops_controller_spec.rb +++ b/spec/controllers/crops_controller_spec.rb @@ -5,9 +5,9 @@ describe CropsController do def valid_attributes { - name: "Tomato", + name: "Tomato", en_wikipedia_url: 'http://en.wikipedia.org/wiki/Tomato', - approval_status: 'approved' + approval_status: 'approved' } end diff --git a/spec/controllers/forums_controller_spec.rb b/spec/controllers/forums_controller_spec.rb index 5c59473cb..aeca681c1 100644 --- a/spec/controllers/forums_controller_spec.rb +++ b/spec/controllers/forums_controller_spec.rb @@ -5,9 +5,9 @@ describe ForumsController do def valid_attributes { - "name" => "MyString", + "name" => "MyString", "description" => "Something", - "owner_id" => 1 + "owner_id" => 1 } end diff --git a/spec/controllers/harvests_controller_spec.rb b/spec/controllers/harvests_controller_spec.rb index 186782e88..aad371da5 100644 --- a/spec/controllers/harvests_controller_spec.rb +++ b/spec/controllers/harvests_controller_spec.rb @@ -5,10 +5,10 @@ describe HarvestsController do def valid_attributes { - owner_id: subject.current_member.id, - crop_id: FactoryBot.create(:crop).id, + owner_id: subject.current_member.id, + crop_id: FactoryBot.create(:crop).id, plant_part_id: FactoryBot.create(:plant_part).id, - harvested_at: '2017-01-01' + harvested_at: '2017-01-01' } end @@ -176,7 +176,7 @@ describe HarvestsController do describe "does not save planting_id" do before do - put :update, params: { id: harvest.to_param, + put :update, params: { id: harvest.to_param, harvest: valid_attributes.merge(planting_id: not_my_planting.id) } end diff --git a/spec/controllers/notifications_controller_spec.rb b/spec/controllers/notifications_controller_spec.rb index 30cf37ddd..55ba4b947 100644 --- a/spec/controllers/notifications_controller_spec.rb +++ b/spec/controllers/notifications_controller_spec.rb @@ -6,8 +6,8 @@ describe NotificationsController do def valid_attributes { "recipient_id" => subject.current_member.id, - "sender_id" => FactoryBot.create(:member).id, - "subject" => 'test' + "sender_id" => FactoryBot.create(:member).id, + "subject" => 'test' } end @@ -18,9 +18,9 @@ describe NotificationsController do # attributes. def valid_attributes_for_sender { - "sender_id" => subject.current_member.id, + "sender_id" => subject.current_member.id, "recipient_id" => FactoryBot.create(:member).id, - "subject" => 'test' + "subject" => 'test' } end diff --git a/spec/controllers/photo_associations_controller_spec.rb b/spec/controllers/photo_associations_controller_spec.rb index 5ed022218..a7c05f029 100644 --- a/spec/controllers/photo_associations_controller_spec.rb +++ b/spec/controllers/photo_associations_controller_spec.rb @@ -6,8 +6,8 @@ describe PhotoAssociationsController do describe "destroy" do let(:valid_params) do { - id: harvest.id, - type: 'harvest', + id: harvest.id, + type: 'harvest', photo_id: photo.id } end diff --git a/spec/controllers/photos_controller_spec.rb b/spec/controllers/photos_controller_spec.rb index 88ed6599b..e49d3b194 100644 --- a/spec/controllers/photos_controller_spec.rb +++ b/spec/controllers/photos_controller_spec.rb @@ -8,13 +8,13 @@ describe PhotosController do def valid_attributes member = FactoryBot.create(:member) { - "owner_id" => member.id, + "owner_id" => member.id, "flickr_photo_id" => 1, - "title" => "Photo", - "license_name" => "CC-BY", - "thumbnail_url" => 'http://example.com/thumb.jpg', - "fullsize_url" => 'http://example.com/full.jpg', - "link_url" => 'http://example.com' + "title" => "Photo", + "license_name" => "CC-BY", + "thumbnail_url" => 'http://example.com/thumb.jpg', + "fullsize_url" => 'http://example.com/full.jpg', + "link_url" => 'http://example.com' } end @@ -60,12 +60,12 @@ describe PhotosController do describe "POST create" do before(:each) do - Photo.any_instance.stub(:flickr_metadata).and_return(title: "A Heartbreaking work of staggering genius", - license_name: "CC-BY", - license_url: "http://example.com/aybpl", + Photo.any_instance.stub(:flickr_metadata).and_return(title: "A Heartbreaking work of staggering genius", + license_name: "CC-BY", + license_url: "http://example.com/aybpl", thumbnail_url: "http://example.com/thumb.jpg", - fullsize_url: "http://example.com/full.jpg", - link_url: "http://example.com") + fullsize_url: "http://example.com/full.jpg", + link_url: "http://example.com") end let(:member) { FactoryBot.create(:member) } diff --git a/spec/controllers/plantings_controller_spec.rb b/spec/controllers/plantings_controller_spec.rb index 7f100d76b..e2c6522be 100644 --- a/spec/controllers/plantings_controller_spec.rb +++ b/spec/controllers/plantings_controller_spec.rb @@ -6,7 +6,7 @@ describe PlantingsController do def valid_attributes { garden_id: FactoryBot.create(:garden, owner: subject.current_member).id, - crop_id: FactoryBot.create(:crop).id + crop_id: FactoryBot.create(:crop).id } end diff --git a/spec/features/crops/crop_detail_page_spec.rb b/spec/features/crops/crop_detail_page_spec.rb index a194e8f1e..cbc73cf9c 100644 --- a/spec/features/crops/crop_detail_page_spec.rb +++ b/spec/features/crops/crop_detail_page_spec.rb @@ -200,8 +200,8 @@ feature "crop detail page", js: true do context 'predictions' do let!(:planting) do - FactoryBot.create(:planting, crop: crop, - planted_at: 100.days.ago, + FactoryBot.create(:planting, crop: crop, + planted_at: 100.days.ago, finished_at: 1.day.ago) end diff --git a/spec/features/crops/crop_photos_spec.rb b/spec/features/crops/crop_photos_spec.rb index efefc6a36..36138fed6 100644 --- a/spec/features/crops/crop_photos_spec.rb +++ b/spec/features/crops/crop_photos_spec.rb @@ -10,27 +10,27 @@ feature "crop detail page", js: true do let(:harvest) { create :harvest, owner: member } let(:valid_server) { 'https://farm5.staticflickr.com/' } let(:photo1) do - create(:photo, owner: member, - title: 'photo 1', - fullsize_url: "#{valid_server}photo1.jpg", + create(:photo, owner: member, + title: 'photo 1', + fullsize_url: "#{valid_server}photo1.jpg", thumbnail_url: "#{valid_server}thumb1.jpg") end let(:photo2) do - create(:photo, owner: member, - title: 'photo 2', - fullsize_url: "#{valid_server}photo2.jpg", + create(:photo, owner: member, + title: 'photo 2', + fullsize_url: "#{valid_server}photo2.jpg", thumbnail_url: "#{valid_server}thumb2.jpg") end let(:photo3) do - create(:photo, owner: member, - title: 'photo 3', - fullsize_url: "#{valid_server}photo3.jpg", + create(:photo, owner: member, + title: 'photo 3', + fullsize_url: "#{valid_server}photo3.jpg", thumbnail_url: "#{valid_server}thumb3.jpg") end let(:photo4) do - create(:photo, owner: member, - title: 'photo 4', - fullsize_url: "#{valid_server}photo4.jpg", + create(:photo, owner: member, + title: 'photo 4', + fullsize_url: "#{valid_server}photo4.jpg", thumbnail_url: "#{valid_server}thumb4.jpg") end diff --git a/spec/features/gardens/gardens_index_spec.rb b/spec/features/gardens/gardens_index_spec.rb index 03fc28447..ebe5f5547 100644 --- a/spec/features/gardens/gardens_index_spec.rb +++ b/spec/features/gardens/gardens_index_spec.rb @@ -79,10 +79,10 @@ feature "Gardens#index", :js do # time to finished = 90 days FactoryBot.create(:harvest, harvested_at: 50.days.ago, - crop: crop, - planting: FactoryBot.create(:planting, - crop: crop, - planted_at: 100.days.ago, + crop: crop, + planting: FactoryBot.create(:planting, + crop: crop, + planted_at: 100.days.ago, finished_at: 10.days.ago)) crop.plantings.each(&:update_harvest_days!) crop.update_lifespan_medians @@ -96,9 +96,9 @@ feature "Gardens#index", :js do describe 'harvest still growing' do let!(:planting) do FactoryBot.create :planting, - crop: crop, - owner: member, - garden: garden, + crop: crop, + owner: member, + garden: garden, planted_at: Time.zone.today end diff --git a/spec/features/notifications_spec.rb b/spec/features/notifications_spec.rb index a3049a0c2..05e6e2b9a 100644 --- a/spec/features/notifications_spec.rb +++ b/spec/features/notifications_spec.rb @@ -7,10 +7,10 @@ feature "Notifications", :js do context "On existing notification" do let!(:notification) do create :notification, - sender: sender, + sender: sender, recipient: recipient, - body: "Notification body", - post_id: nil + body: "Notification body", + post_id: nil end background do diff --git a/spec/helpers/harvests_helper_spec.rb b/spec/helpers/harvests_helper_spec.rb index 1ba63810d..d720f1318 100644 --- a/spec/helpers/harvests_helper_spec.rb +++ b/spec/helpers/harvests_helper_spec.rb @@ -4,7 +4,7 @@ describe HarvestsHelper do describe "display_quantity" do it "blank" do harvest = FactoryBot.create(:harvest, - quantity: nil, + quantity: nil, weight_quantity: nil) result = helper.display_quantity(harvest) result.should eq 'not specified' @@ -12,8 +12,8 @@ describe HarvestsHelper do it '3 individual' do harvest = FactoryBot.create(:harvest, - quantity: 3, - unit: 'individual', + quantity: 3, + unit: 'individual', weight_quantity: nil) result = helper.display_quantity(harvest) result.should eq '3' @@ -21,8 +21,8 @@ describe HarvestsHelper do it '1 bunch' do harvest = FactoryBot.create(:harvest, - quantity: 1, - unit: 'bunch', + quantity: 1, + unit: 'bunch', weight_quantity: nil) result = helper.display_quantity(harvest) result.should eq '1 bunch' @@ -30,8 +30,8 @@ describe HarvestsHelper do it '3 bunches' do harvest = FactoryBot.create(:harvest, - quantity: 3, - unit: 'bunch', + quantity: 3, + unit: 'bunch', weight_quantity: nil) result = helper.display_quantity(harvest) result.should eq '3 bunches' @@ -39,30 +39,30 @@ describe HarvestsHelper do it '3 kg' do harvest = FactoryBot.create(:harvest, - quantity: nil, - unit: nil, + quantity: nil, + unit: nil, weight_quantity: 3, - weight_unit: 'kg') + weight_unit: 'kg') result = helper.display_quantity(harvest) result.should eq '3 kg' end it '3 individual weighing 3 kg' do harvest = FactoryBot.create(:harvest, - quantity: 3, - unit: 'individual', + quantity: 3, + unit: 'individual', weight_quantity: 3, - weight_unit: 'kg') + weight_unit: 'kg') result = helper.display_quantity(harvest) result.should eq '3, weighing 3 kg' end it '3 bunches weighing 3 kg' do harvest = FactoryBot.create(:harvest, - quantity: 3, - unit: 'bunch', + quantity: 3, + unit: 'bunch', weight_quantity: 3, - weight_unit: 'kg') + weight_unit: 'kg') result = helper.display_quantity(harvest) result.should eq '3 bunches, weighing 3 kg' end diff --git a/spec/helpers/plantings_helper_spec.rb b/spec/helpers/plantings_helper_spec.rb index 3af084d5a..f71fc6ef4 100644 --- a/spec/helpers/plantings_helper_spec.rb +++ b/spec/helpers/plantings_helper_spec.rb @@ -6,18 +6,18 @@ describe PlantingsHelper do it "does not have a quantity nor a planted from value provided" do planting = FactoryBot.build(:planting, - quantity: nil, + quantity: nil, planted_from: '', - owner: member) + owner: member) result = helper.display_planting(planting) expect(result).to eq "crop_lady." end it "does not have a quantity provided" do planting = FactoryBot.build(:planting, - quantity: nil, + quantity: nil, planted_from: 'seed', - owner: member) + owner: member) result = helper.display_planting(planting) expect(result).to eq "crop_lady planted seeds." end @@ -25,18 +25,18 @@ describe PlantingsHelper do context "when quantity is greater than 1" do it "does not have a planted from value provided" do planting = FactoryBot.build(:planting, - quantity: 10, + quantity: 10, planted_from: '', - owner: member) + owner: member) result = helper.display_planting(planting) expect(result).to eq "crop_lady planted 10 units." end it "does have a planted from value provided" do planting = FactoryBot.build(:planting, - quantity: 5, + quantity: 5, planted_from: 'seed', - owner: member) + owner: member) result = helper.display_planting(planting) expect(result).to eq "crop_lady planted 5 seeds." end @@ -45,18 +45,18 @@ describe PlantingsHelper do context "when quantity is 1" do it "does not have a planted from value provided" do planting = FactoryBot.build(:planting, - quantity: 1, + quantity: 1, planted_from: '', - owner: member) + owner: member) result = helper.display_planting(planting) expect(result).to eq "crop_lady planted 1 unit." end it "does have a planted from value provided" do planting = FactoryBot.build(:planting, - quantity: 1, + quantity: 1, planted_from: 'seed', - owner: member) + owner: member) result = helper.display_planting(planting) expect(result).to eq "crop_lady planted 1 seed." end diff --git a/spec/lib/actions/oauth_signup_action_spec.rb b/spec/lib/actions/oauth_signup_action_spec.rb index 1710428a4..3fc37c00e 100644 --- a/spec/lib/actions/oauth_signup_action_spec.rb +++ b/spec/lib/actions/oauth_signup_action_spec.rb @@ -8,16 +8,16 @@ describe 'Growstuff::OauthSignupAction' do context 'with a valid authentication' do before :each do - @auth = OmniAuth::AuthHash.new('provider' => 'facebook', - 'uid' => '123545', - 'info' => { - 'name' => "John Testerson's Brother", + @auth = OmniAuth::AuthHash.new('provider' => 'facebook', + 'uid' => '123545', + 'info' => { + 'name' => "John Testerson's Brother", 'nickname' => 'JohnnyB', - 'email' => 'example.oauth.facebook@example.com', - 'image' => 'http://findicons.com/files/icons/1072/face_avatars/300/i04.png' + 'email' => 'example.oauth.facebook@example.com', + 'image' => 'http://findicons.com/files/icons/1072/face_avatars/300/i04.png' }, 'credentials' => { - 'token' => "token", + 'token' => "token", 'secret' => "donttell" }) end @@ -74,8 +74,8 @@ describe 'Growstuff::OauthSignupAction' do @auth['info']['email'] = 'never.used.oauth@yahoo.com' Member.where(email: @auth['info']['email']).delete_all - @existing_member = create :member, email: @auth['info']['email'], - login_name: 'existing', + @existing_member = create :member, email: @auth['info']['email'], + login_name: 'existing', preferred_avatar_uri: 'http://cl.jroo.me/z3/W/H/K/e/a.baa-very-cool-hat-you-.jpg' @member = @action.find_or_create_from_authorization(@auth) @@ -118,13 +118,13 @@ describe 'Growstuff::OauthSignupAction' do Member.where(email: @auth['info']['email']).delete_all Authentication.delete_all - @existing_member = create :member, email: @auth['info']['email'], - login_name: 'schrodingerscat', + @existing_member = create :member, email: @auth['info']['email'], + login_name: 'schrodingerscat', preferred_avatar_uri: 'http://cl.jroo.me/z3/W/H/K/e/a.baa-very-cool-hat-you-.jpg' - @existing_authentication = @existing_member.authentications.create(provider: 'facebook', - uid: '123545', - name: "John Testerson's Brother", + @existing_authentication = @existing_member.authentications.create(provider: 'facebook', + uid: '123545', + name: "John Testerson's Brother", member_id: @existing_member.id) @member = @action.find_or_create_from_authorization(@auth) diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index e95dfbdc6..7891135c9 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -20,13 +20,13 @@ describe Ability do ability.should_not be_able_to(:create, FactoryBot.create(:notification, recipient: member, - sender: member)) + sender: member)) end it "member can send messages to someone else" do ability.should be_able_to(:create, FactoryBot.create(:notification, recipient: FactoryBot.create(:member), - sender: member)) + sender: member)) end end diff --git a/spec/models/alternate_name_spec.rb b/spec/models/alternate_name_spec.rb index a76749130..42b8aa9aa 100644 --- a/spec/models/alternate_name_spec.rb +++ b/spec/models/alternate_name_spec.rb @@ -10,8 +10,8 @@ describe AlternateName do it 'should be possible to add multiple alternate names to a crop' do crop = an.crop an2 = AlternateName.create( - name: "really alternative tomato", - crop_id: crop.id, + name: "really alternative tomato", + crop_id: crop.id, creator_id: an.creator.id ) crop.alternate_names << an2 diff --git a/spec/models/crop_spec.rb b/spec/models/crop_spec.rb index a52e6c583..1b8de81ed 100644 --- a/spec/models/crop_spec.rb +++ b/spec/models/crop_spec.rb @@ -259,20 +259,20 @@ describe Crop do @root = FactoryBot.create(:plant_part, name: 'root') @bulb = FactoryBot.create(:plant_part, name: 'bulb') @harvest1 = FactoryBot.create(:harvest, - crop: crop, + crop: crop, plant_part: @fruit) @harvest2 = FactoryBot.create(:harvest, - crop: crop, + crop: crop, plant_part: @fruit) @harvest3 = FactoryBot.create(:harvest, - crop: crop, + crop: crop, plant_part: @seed) @harvest4 = FactoryBot.create(:harvest, - crop: crop, + crop: crop, plant_part: @root) crop.popular_plant_parts.should == { [@fruit.id, @fruit.name] => 2, - [@seed.id, @seed.name] => 1, - [@root.id, @root.name] => 1 } + [@seed.id, @seed.name] => 1, + [@root.id, @root.name] => 1 } end end @@ -564,15 +564,15 @@ describe Crop do context "crop rejections" do let!(:rejected_reason) do - FactoryBot.create(:crop, name: 'tomato', - approval_status: 'rejected', + FactoryBot.create(:crop, name: 'tomato', + approval_status: 'rejected', reason_for_rejection: 'not edible') end let!(:rejected_other) do - FactoryBot.create(:crop, name: 'tomato', - approval_status: 'rejected', + FactoryBot.create(:crop, name: 'tomato', + approval_status: 'rejected', reason_for_rejection: 'other', - rejection_notes: 'blah blah blah') + rejection_notes: 'blah blah blah') end describe "rejecting a crop" do diff --git a/spec/models/harvest_spec.rb b/spec/models/harvest_spec.rb index 455afeb9b..9711d2a59 100644 --- a/spec/models/harvest_spec.rb +++ b/spec/models/harvest_spec.rb @@ -150,74 +150,74 @@ describe Harvest do let(:crop) { FactoryBot.create(:crop, name: "apricot") } it "apricots" do - @h = FactoryBot.create(:harvest, crop: crop, - quantity: nil, - unit: nil, + @h = FactoryBot.create(:harvest, crop: crop, + quantity: nil, + unit: nil, weight_quantity: nil, - weight_unit: nil) + weight_unit: nil) expect(@h.to_s).to eq "apricots" end it "1 individual apricot" do - @h = FactoryBot.create(:harvest, crop: crop, - quantity: 1, - unit: 'individual', + @h = FactoryBot.create(:harvest, crop: crop, + quantity: 1, + unit: 'individual', weight_quantity: nil, - weight_unit: nil) + weight_unit: nil) expect(@h.to_s).to eq "1 individual apricot" end it "10 individual apricots" do - @h = FactoryBot.create(:harvest, crop: crop, - quantity: 10, - unit: 'individual', + @h = FactoryBot.create(:harvest, crop: crop, + quantity: 10, + unit: 'individual', weight_quantity: nil, - weight_unit: nil) + weight_unit: nil) expect(@h.to_s).to eq "10 individual apricots" end it "1 bushel of apricots" do - @h = FactoryBot.create(:harvest, crop: crop, - quantity: 1, - unit: 'bushel', + @h = FactoryBot.create(:harvest, crop: crop, + quantity: 1, + unit: 'bushel', weight_quantity: nil, - weight_unit: nil) + weight_unit: nil) expect(@h.to_s).to eq "1 bushel of apricots" end it "1.5 bushels of apricots" do - @h = FactoryBot.create(:harvest, crop: crop, - quantity: 1.5, - unit: 'bushel', + @h = FactoryBot.create(:harvest, crop: crop, + quantity: 1.5, + unit: 'bushel', weight_quantity: nil, - weight_unit: nil) + weight_unit: nil) expect(@h.to_s).to eq "1.5 bushels of apricots" end it "10 bushels of apricots" do - @h = FactoryBot.create(:harvest, crop: crop, - quantity: 10, - unit: 'bushel', + @h = FactoryBot.create(:harvest, crop: crop, + quantity: 10, + unit: 'bushel', weight_quantity: nil, - weight_unit: nil) + weight_unit: nil) expect(@h.to_s).to eq "10 bushels of apricots" end it "apricots weighing 1.2 kg" do - @h = FactoryBot.create(:harvest, crop: crop, - quantity: nil, - unit: nil, + @h = FactoryBot.create(:harvest, crop: crop, + quantity: nil, + unit: nil, weight_quantity: 1.2, - weight_unit: 'kg') + weight_unit: 'kg') expect(@h.to_s).to eq "apricots weighing 1.2 kg" end it "10 bushels of apricots weighing 100 kg" do - @h = FactoryBot.create(:harvest, crop: crop, - quantity: 10, - unit: 'bushel', + @h = FactoryBot.create(:harvest, crop: crop, + quantity: 10, + unit: 'bushel', weight_quantity: 100, - weight_unit: 'kg') + weight_unit: 'kg') expect(@h.to_s).to eq "10 bushels of apricots weighing 100 kg" end end diff --git a/spec/models/plant_part_spec.rb b/spec/models/plant_part_spec.rb index a6ec189dd..66612e5ad 100644 --- a/spec/models/plant_part_spec.rb +++ b/spec/models/plant_part_spec.rb @@ -11,10 +11,10 @@ describe PlantPart do @tomato = FactoryBot.create(:tomato) @pp1 = FactoryBot.create(:plant_part) @h1 = FactoryBot.create(:harvest, - crop: @tomato, + crop: @tomato, plant_part: @pp1) @h2 = FactoryBot.create(:harvest, - crop: @maize, + crop: @maize, plant_part: @pp1) @pp1.crops.should include @tomato @pp1.crops.should include @maize @@ -24,10 +24,10 @@ describe PlantPart do @maize = FactoryBot.create(:maize) @pp1 = FactoryBot.create(:plant_part) @h1 = FactoryBot.create(:harvest, - crop: @maize, + crop: @maize, plant_part: @pp1) @h2 = FactoryBot.create(:harvest, - crop: @maize, + crop: @maize, plant_part: @pp1) @pp1.crops.should eq [@maize] end diff --git a/spec/models/planting_spec.rb b/spec/models/planting_spec.rb index 6647655ca..29241db3f 100644 --- a/spec/models/planting_spec.rb +++ b/spec/models/planting_spec.rb @@ -180,8 +180,8 @@ describe Planting do before do FactoryBot.create(:harvest, - planting: planting, - crop: planting.crop, + planting: planting, + crop: planting.crop, harvested_at: 10.days.ago) planting.update_harvest_days! planting.crop.update_harvest_medians @@ -402,8 +402,8 @@ describe Planting do # this one is newer, and has the same owner, through the garden @planting2 = FactoryBot.create(:planting, created_at: 1.minute.ago, - garden: @planting1.garden, - owner: @planting1.owner) + garden: @planting1.garden, + owner: @planting1.owner) @planting2.photos << FactoryBot.create(:photo, owner: @planting2.owner) @planting2.save diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index 6c2d9b32e..eeda089d2 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -70,7 +70,7 @@ describe Post do end it "sets recent activity to comment time" do - comment = FactoryBot.create(:comment, post: post, + comment = FactoryBot.create(:comment, post: post, created_at: 1.hour.ago) post.recent_activity.to_i.should eq comment.created_at.to_i end diff --git a/spec/requests/api/v1/crop_request_spec.rb b/spec/requests/api/v1/crop_request_spec.rb index d0f1948fa..31212ab2c 100644 --- a/spec/requests/api/v1/crop_request_spec.rb +++ b/spec/requests/api/v1/crop_request_spec.rb @@ -6,15 +6,15 @@ RSpec.describe 'Plantings', type: :request do let(:headers) { { 'Accept' => 'application/vnd.api+json' } } let!(:crop) { FactoryBot.create :crop } let(:crop_encoded_as_json_api) do - { "id" => crop.id.to_s, - "type" => "crops", - "links" => { "self" => resource_url }, - "attributes" => attributes, + { "id" => crop.id.to_s, + "type" => "crops", + "links" => { "self" => resource_url }, + "attributes" => attributes, "relationships" => { "plantings" => plantings_as_json_api, - "parent" => parent_as_json_api, - "photos" => photos_as_json_api, - "harvests" => harvests_as_json_api + "parent" => parent_as_json_api, + "photos" => photos_as_json_api, + "harvests" => harvests_as_json_api } } end @@ -22,37 +22,37 @@ RSpec.describe 'Plantings', type: :request do let(:harvests_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/harvests", - "related" => "#{resource_url}/harvests" } } + { "self" => "#{resource_url}/relationships/harvests", + "related" => "#{resource_url}/harvests" } } end let(:parent_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/parent", - "related" => "#{resource_url}/parent" } } + { "self" => "#{resource_url}/relationships/parent", + "related" => "#{resource_url}/parent" } } end let(:plantings_as_json_api) do { "links" => - { "self" => - "#{resource_url}/relationships/plantings", - "related" => "#{resource_url}/plantings" } } + { "self" => + "#{resource_url}/relationships/plantings", + "related" => "#{resource_url}/plantings" } } end let(:photos_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/photos", - "related" => "#{resource_url}/photos" } } + { "self" => "#{resource_url}/relationships/photos", + "related" => "#{resource_url}/photos" } } end let(:attributes) do { - "name" => crop.name, - "en-wikipedia-url" => crop.en_wikipedia_url, - "perennial" => false, - "median-lifespan" => nil, + "name" => crop.name, + "en-wikipedia-url" => crop.en_wikipedia_url, + "perennial" => false, + "median-lifespan" => nil, "median-days-to-first-harvest" => nil, - "median-days-to-last-harvest" => nil + "median-days-to-last-harvest" => nil } end diff --git a/spec/requests/api/v1/gardens_request_spec.rb b/spec/requests/api/v1/gardens_request_spec.rb index 60283f7b7..8426dfcb2 100644 --- a/spec/requests/api/v1/gardens_request_spec.rb +++ b/spec/requests/api/v1/gardens_request_spec.rb @@ -6,36 +6,36 @@ RSpec.describe 'Gardens', type: :request do let(:headers) { { 'Accept' => 'application/vnd.api+json' } } let!(:garden) { FactoryBot.create :garden } let(:garden_encoded_as_json_api) do - { "id" => garden.id.to_s, - "type" => "gardens", - "links" => { "self" => resource_url }, - "attributes" => { "name" => garden.name }, + { "id" => garden.id.to_s, + "type" => "gardens", + "links" => { "self" => resource_url }, + "attributes" => { "name" => garden.name }, "relationships" => - { - "owner" => owner_as_json_api, - "plantings" => plantings_as_json_api, - "photos" => photos_as_json_api - } } + { + "owner" => owner_as_json_api, + "plantings" => plantings_as_json_api, + "photos" => photos_as_json_api + } } end let(:resource_url) { "http://www.example.com/api/v1/gardens/#{garden.id}" } let(:plantings_as_json_api) do { "links" => - { "self" => - "#{resource_url}/relationships/plantings", - "related" => "#{resource_url}/plantings" } } + { "self" => + "#{resource_url}/relationships/plantings", + "related" => "#{resource_url}/plantings" } } end let(:owner_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/owner", - "related" => "#{resource_url}/owner" } } + { "self" => "#{resource_url}/relationships/owner", + "related" => "#{resource_url}/owner" } } end let(:photos_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/photos", - "related" => "#{resource_url}/photos" } } + { "self" => "#{resource_url}/relationships/photos", + "related" => "#{resource_url}/photos" } } end scenario '#index' do diff --git a/spec/requests/api/v1/harvest_request_spec.rb b/spec/requests/api/v1/harvest_request_spec.rb index 595774694..c4fddd350 100644 --- a/spec/requests/api/v1/harvest_request_spec.rb +++ b/spec/requests/api/v1/harvest_request_spec.rb @@ -6,15 +6,15 @@ RSpec.describe 'Harvests', type: :request do let(:headers) { { 'Accept' => 'application/vnd.api+json' } } let!(:harvest) { FactoryBot.create :harvest } let(:harvest_encoded_as_json_api) do - { "id" => harvest.id.to_s, - "type" => "harvests", - "links" => { "self" => resource_url }, - "attributes" => attributes, + { "id" => harvest.id.to_s, + "type" => "harvests", + "links" => { "self" => resource_url }, + "attributes" => attributes, "relationships" => { - "crop" => crop_as_json_api, + "crop" => crop_as_json_api, "planting" => planting_as_json_api, - "owner" => owner_as_json_api, - "photos" => photos_as_json_api + "owner" => owner_as_json_api, + "photos" => photos_as_json_api } } end @@ -22,38 +22,38 @@ RSpec.describe 'Harvests', type: :request do let(:crop_as_json_api) do { "links" => - { "self" => - "#{resource_url}/relationships/crop", - "related" => "#{resource_url}/crop" } } + { "self" => + "#{resource_url}/relationships/crop", + "related" => "#{resource_url}/crop" } } end let(:owner_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/owner", - "related" => "#{resource_url}/owner" } } + { "self" => "#{resource_url}/relationships/owner", + "related" => "#{resource_url}/owner" } } end let(:planting_as_json_api) do { "links" => - { "self" => - "#{resource_url}/relationships/planting", - "related" => "#{resource_url}/planting" } } + { "self" => + "#{resource_url}/relationships/planting", + "related" => "#{resource_url}/planting" } } end let(:photos_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/photos", - "related" => "#{resource_url}/photos" } } + { "self" => "#{resource_url}/relationships/photos", + "related" => "#{resource_url}/photos" } } end let(:attributes) do { - "harvested-at" => "2015-09-17", - "description" => harvest.description, - "unit" => harvest.unit, + "harvested-at" => "2015-09-17", + "description" => harvest.description, + "unit" => harvest.unit, "weight-quantity" => harvest.weight_quantity.to_s, - "weight-unit" => harvest.weight_unit, - "si-weight" => harvest.si_weight + "weight-unit" => harvest.weight_unit, + "si-weight" => harvest.si_weight } end diff --git a/spec/requests/api/v1/member_request_spec.rb b/spec/requests/api/v1/member_request_spec.rb index 334a18fd9..f44b2a4ae 100644 --- a/spec/requests/api/v1/member_request_spec.rb +++ b/spec/requests/api/v1/member_request_spec.rb @@ -6,16 +6,16 @@ RSpec.describe 'Members', type: :request do let(:headers) { { 'Accept' => 'application/vnd.api+json' } } let!(:member) { FactoryBot.create :member } let(:member_encoded_as_json_api) do - { "id" => member.id.to_s, - "type" => "members", - "links" => { "self" => resource_url }, - "attributes" => attributes, + { "id" => member.id.to_s, + "type" => "members", + "links" => { "self" => resource_url }, + "attributes" => attributes, "relationships" => { - "gardens" => gardens_as_json_api, - "harvests" => harvests_as_json_api, - "photos" => photos_as_json_api, + "gardens" => gardens_as_json_api, + "harvests" => harvests_as_json_api, + "photos" => photos_as_json_api, "plantings" => plantings_as_json_api, - "seeds" => seeds_as_json_api + "seeds" => seeds_as_json_api } } end @@ -23,32 +23,32 @@ RSpec.describe 'Members', type: :request do let(:harvests_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/harvests", - "related" => "#{resource_url}/harvests" } } + { "self" => "#{resource_url}/relationships/harvests", + "related" => "#{resource_url}/harvests" } } end let(:photos_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/photos", - "related" => "#{resource_url}/photos" } } + { "self" => "#{resource_url}/relationships/photos", + "related" => "#{resource_url}/photos" } } end let(:seeds_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/seeds", - "related" => "#{resource_url}/seeds" } } + { "self" => "#{resource_url}/relationships/seeds", + "related" => "#{resource_url}/seeds" } } end let(:plantings_as_json_api) do { "links" => - { "self" => - "#{resource_url}/relationships/plantings", - "related" => "#{resource_url}/plantings" } } + { "self" => + "#{resource_url}/relationships/plantings", + "related" => "#{resource_url}/plantings" } } end let(:gardens_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/gardens", - "related" => "#{resource_url}/gardens" } } + { "self" => "#{resource_url}/relationships/gardens", + "related" => "#{resource_url}/gardens" } } end let(:attributes) do @@ -80,7 +80,7 @@ RSpec.describe 'Members', type: :request do it '#update' do expect do - post "/api/v1/members/#{member.id}", params: { + post "/api/v1/members/#{member.id}", params: { 'member' => { 'login_name' => 'can i modify this' } }, headers: headers diff --git a/spec/requests/api/v1/photos_request_spec.rb b/spec/requests/api/v1/photos_request_spec.rb index 7ae939562..15fb8cd19 100644 --- a/spec/requests/api/v1/photos_request_spec.rb +++ b/spec/requests/api/v1/photos_request_spec.rb @@ -6,15 +6,15 @@ RSpec.describe 'Photos', type: :request do let(:headers) { { 'Accept' => 'application/vnd.api+json' } } let!(:photo) { FactoryBot.create :photo } let(:photo_encoded_as_json_api) do - { "id" => photo.id.to_s, - "type" => "photos", - "links" => { "self" => resource_url }, - "attributes" => attributes, + { "id" => photo.id.to_s, + "type" => "photos", + "links" => { "self" => resource_url }, + "attributes" => attributes, "relationships" => { - "owner" => owner_as_json_api, + "owner" => owner_as_json_api, "plantings" => plantings_as_json_api, - "harvests" => harvests_as_json_api, - "gardens" => gardens_as_json_api + "harvests" => harvests_as_json_api, + "gardens" => gardens_as_json_api } } end @@ -22,36 +22,36 @@ RSpec.describe 'Photos', type: :request do let(:owner_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/owner", - "related" => "#{resource_url}/owner" } } + { "self" => "#{resource_url}/relationships/owner", + "related" => "#{resource_url}/owner" } } end let(:harvests_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/harvests", - "related" => "#{resource_url}/harvests" } } + { "self" => "#{resource_url}/relationships/harvests", + "related" => "#{resource_url}/harvests" } } end let(:gardens_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/gardens", - "related" => "#{resource_url}/gardens" } } + { "self" => "#{resource_url}/relationships/gardens", + "related" => "#{resource_url}/gardens" } } end let(:plantings_as_json_api) do { "links" => - { "self" => - "#{resource_url}/relationships/plantings", - "related" => "#{resource_url}/plantings" } } + { "self" => + "#{resource_url}/relationships/plantings", + "related" => "#{resource_url}/plantings" } } end let(:attributes) do { "thumbnail-url" => photo.thumbnail_url, - "fullsize-url" => photo.fullsize_url, - "link-url" => photo.link_url, - "license-name" => photo.license_name, - "title" => photo.title + "fullsize-url" => photo.fullsize_url, + "link-url" => photo.link_url, + "license-name" => photo.license_name, + "title" => photo.title } end diff --git a/spec/requests/api/v1/plantings_request_spec.rb b/spec/requests/api/v1/plantings_request_spec.rb index 24085b00c..0291af4b2 100644 --- a/spec/requests/api/v1/plantings_request_spec.rb +++ b/spec/requests/api/v1/plantings_request_spec.rb @@ -6,15 +6,15 @@ RSpec.describe 'Plantings', type: :request do let(:headers) { { 'Accept' => 'application/vnd.api+json' } } let!(:planting) { FactoryBot.create :planting } let(:planting_encoded_as_json_api) do - { "id" => planting.id.to_s, - "type" => "plantings", - "links" => { "self" => resource_url }, - "attributes" => attributes, + { "id" => planting.id.to_s, + "type" => "plantings", + "links" => { "self" => resource_url }, + "attributes" => attributes, "relationships" => { - "garden" => garden_as_json_api, - "crop" => crop_as_json_api, - "owner" => owner_as_json_api, - "photos" => photos_as_json_api, + "garden" => garden_as_json_api, + "crop" => crop_as_json_api, + "owner" => owner_as_json_api, + "photos" => photos_as_json_api, "harvests" => harvests_as_json_api } } end @@ -23,48 +23,48 @@ RSpec.describe 'Plantings', type: :request do let(:harvests_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/harvests", - "related" => "#{resource_url}/harvests" } } + { "self" => "#{resource_url}/relationships/harvests", + "related" => "#{resource_url}/harvests" } } end let(:photos_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/photos", - "related" => "#{resource_url}/photos" } } + { "self" => "#{resource_url}/relationships/photos", + "related" => "#{resource_url}/photos" } } end let(:owner_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/owner", - "related" => "#{resource_url}/owner" } } + { "self" => "#{resource_url}/relationships/owner", + "related" => "#{resource_url}/owner" } } end let(:crop_as_json_api) do { "links" => - { "self" => - "#{resource_url}/relationships/crop", - "related" => "#{resource_url}/crop" } } + { "self" => + "#{resource_url}/relationships/crop", + "related" => "#{resource_url}/crop" } } end let(:garden_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/garden", - "related" => "#{resource_url}/garden" } } + { "self" => "#{resource_url}/relationships/garden", + "related" => "#{resource_url}/garden" } } end let(:attributes) do { - "planted-at" => "2014-07-30", - "finished-at" => nil, - "finished" => false, - "quantity" => 33, - "description" => planting.description, - "sunniness" => nil, - "planted-from" => nil, - "expected-lifespan" => nil, + "planted-at" => "2014-07-30", + "finished-at" => nil, + "finished" => false, + "quantity" => 33, + "description" => planting.description, + "sunniness" => nil, + "planted-from" => nil, + "expected-lifespan" => nil, "finish-predicted-at" => nil, - "percentage-grown" => nil, - "first-harvest-date" => nil, - "last-harvest-date" => nil + "percentage-grown" => nil, + "first-harvest-date" => nil, + "last-harvest-date" => nil } end diff --git a/spec/requests/api/v1/seeds_request_spec.rb b/spec/requests/api/v1/seeds_request_spec.rb index 92cafc0a4..909d08864 100644 --- a/spec/requests/api/v1/seeds_request_spec.rb +++ b/spec/requests/api/v1/seeds_request_spec.rb @@ -6,13 +6,13 @@ RSpec.describe 'Photos', type: :request do let(:headers) { { 'Accept' => 'application/vnd.api+json' } } let!(:seed) { FactoryBot.create :seed } let(:seed_encoded_as_json_api) do - { "id" => seed.id.to_s, - "type" => "seeds", - "links" => { "self" => resource_url }, - "attributes" => attributes, + { "id" => seed.id.to_s, + "type" => "seeds", + "links" => { "self" => resource_url }, + "attributes" => attributes, "relationships" => { "owner" => owner_as_json_api, - "crop" => crop_as_json_api + "crop" => crop_as_json_api } } end @@ -20,27 +20,27 @@ RSpec.describe 'Photos', type: :request do let(:owner_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/owner", - "related" => "#{resource_url}/owner" } } + { "self" => "#{resource_url}/relationships/owner", + "related" => "#{resource_url}/owner" } } end let(:crop_as_json_api) do { "links" => - { "self" => "#{resource_url}/relationships/crop", - "related" => "#{resource_url}/crop" } } + { "self" => "#{resource_url}/relationships/crop", + "related" => "#{resource_url}/crop" } } end let(:attributes) do { - "description" => seed.description, - "quantity" => seed.quantity, - "plant-before" => "2013-07-15", - "tradable-to" => seed.tradable_to, + "description" => seed.description, + "quantity" => seed.quantity, + "plant-before" => "2013-07-15", + "tradable-to" => seed.tradable_to, "days-until-maturity-min" => seed.days_until_maturity_min, "days-until-maturity-max" => seed.days_until_maturity_max, - "organic" => seed.organic, - "gmo" => seed.gmo, - "heirloom" => seed.heirloom + "organic" => seed.organic, + "gmo" => seed.gmo, + "heirloom" => seed.heirloom } end diff --git a/spec/views/crops/_grown_for.html.haml_spec.rb b/spec/views/crops/_grown_for.html.haml_spec.rb index c378c296c..ccb04b28d 100644 --- a/spec/views/crops/_grown_for.html.haml_spec.rb +++ b/spec/views/crops/_grown_for.html.haml_spec.rb @@ -5,7 +5,7 @@ describe "crops/_grown_for" do let(:plant_path) { FactoryBot.create(:plant_part) } let!(:harvest) do FactoryBot.create(:harvest, - crop: crop, + crop: crop, plant_part: plant_path) end diff --git a/spec/views/forums/edit.html.haml_spec.rb b/spec/views/forums/edit.html.haml_spec.rb index 44151170a..77f5cff31 100644 --- a/spec/views/forums/edit.html.haml_spec.rb +++ b/spec/views/forums/edit.html.haml_spec.rb @@ -3,9 +3,9 @@ require 'rails_helper' describe "forums/edit" do before(:each) do @forum = assign(:forum, stub_model(Forum, - name: "MyString", + name: "MyString", description: "MyText", - owner_id: 1)) + owner_id: 1)) end it "renders the edit forum form" do diff --git a/spec/views/harvests/index.html.haml_spec.rb b/spec/views/harvests/index.html.haml_spec.rb index fe205cda6..b5ebcfb67 100644 --- a/spec/views/harvests/index.html.haml_spec.rb +++ b/spec/views/harvests/index.html.haml_spec.rb @@ -13,12 +13,12 @@ describe "harvests/index" do harvests = WillPaginate::Collection.create(page, per_page, total_entries) do |pager| pager.replace([ FactoryBot.create(:harvest, - crop: @tomato, + crop: @tomato, owner: @member), FactoryBot.create(:harvest, - crop: @maize, + crop: @maize, plant_part: @pp, - owner: @member) + owner: @member) ]) end assign(:harvests, harvests) diff --git a/spec/views/harvests/index.rss.haml_spec.rb b/spec/views/harvests/index.rss.haml_spec.rb index aea1f7bc5..b0898cf78 100644 --- a/spec/views/harvests/index.rss.haml_spec.rb +++ b/spec/views/harvests/index.rss.haml_spec.rb @@ -13,13 +13,13 @@ describe 'harvests/index.rss.haml' do harvests = WillPaginate::Collection.create(page, per_page, total_entries) do |pager| pager.replace([ FactoryBot.create(:harvest, - crop: @tomato, + crop: @tomato, owner: @member), FactoryBot.create(:harvest, - crop: @maize, + crop: @maize, plant_part: @pp, - owner: @member, - quantity: 2) + owner: @member, + quantity: 2) ]) end assign(:harvests, harvests) diff --git a/spec/views/notifications/index.html.haml_spec.rb b/spec/views/notifications/index.html.haml_spec.rb index 2c8195069..0bb4257fa 100644 --- a/spec/views/notifications/index.html.haml_spec.rb +++ b/spec/views/notifications/index.html.haml_spec.rb @@ -8,7 +8,7 @@ describe "notifications/index" do context "ordinary notifications" do before(:each) do - @notification = FactoryBot.create(:notification, sender: @member, + @notification = FactoryBot.create(:notification, sender: @member, recipient: @member) assign(:notifications, Kaminari.paginate_array([@notification, @notification]).page(1)) render diff --git a/spec/views/photos/edit.html.haml_spec.rb b/spec/views/photos/edit.html.haml_spec.rb index a52b51a79..d83a1a911 100644 --- a/spec/views/photos/edit.html.haml_spec.rb +++ b/spec/views/photos/edit.html.haml_spec.rb @@ -3,9 +3,9 @@ require 'rails_helper' describe "photos/edit" do before(:each) do @photo = assign(:photo, stub_model(Photo, - owner_id: 1, + owner_id: 1, flickr_photo_id: 1, - thumbnail_url: "MyString", - fullsize_url: "MyString")) + thumbnail_url: "MyString", + fullsize_url: "MyString")) end end diff --git a/spec/views/plantings/_form.html.haml_spec.rb b/spec/views/plantings/_form.html.haml_spec.rb index 6371311ae..8da8f1bfe 100644 --- a/spec/views/plantings/_form.html.haml_spec.rb +++ b/spec/views/plantings/_form.html.haml_spec.rb @@ -9,9 +9,9 @@ describe "plantings/_form" do @crop = @lowercase # needed to render the form @planting = FactoryBot.create(:planting, - garden: @garden, - crop: @crop, - owner: @member, + garden: @garden, + crop: @crop, + owner: @member, planted_at: Date.new(2013, 3, 1)) sign_in @member diff --git a/spec/views/plantings/edit.html.haml_spec.rb b/spec/views/plantings/edit.html.haml_spec.rb index b83af0028..0afa1e829 100644 --- a/spec/views/plantings/edit.html.haml_spec.rb +++ b/spec/views/plantings/edit.html.haml_spec.rb @@ -4,7 +4,7 @@ describe "plantings/edit" do before(:each) do @member = FactoryBot.create(:member, login_name: 'right', - email: 'right@example.com') + email: 'right@example.com') # creating two crops to make sure that the correct one is selected # in the form. diff --git a/spec/views/plantings/index.html.haml_spec.rb b/spec/views/plantings/index.html.haml_spec.rb index a3a713100..3df5b20ff 100644 --- a/spec/views/plantings/index.html.haml_spec.rb +++ b/spec/views/plantings/index.html.haml_spec.rb @@ -15,21 +15,21 @@ describe "plantings/index" do pager.replace([ FactoryBot.create(:planting, garden: garden, - crop: tomato, - owner: member), + crop: tomato, + owner: member), FactoryBot.create(:planting, - garden: garden, - crop: maize, - owner: garden.owner, + garden: garden, + crop: maize, + owner: garden.owner, description: '', - planted_at: Time.zone.local(2013, 1, 13)), + planted_at: Time.zone.local(2013, 1, 13)), FactoryBot.create(:planting, - garden: garden, - owner: garden.owner, - crop: tomato, - planted_at: Time.zone.local(2013, 1, 13), + garden: garden, + owner: garden.owner, + crop: tomato, + planted_at: Time.zone.local(2013, 1, 13), finished_at: Time.zone.local(2013, 1, 20), - finished: true) + finished: true) ]) end assign(:plantings, plantings) diff --git a/spec/views/plantings/new.html.haml_spec.rb b/spec/views/plantings/new.html.haml_spec.rb index 6667a05ee..6b6b11f57 100644 --- a/spec/views/plantings/new.html.haml_spec.rb +++ b/spec/views/plantings/new.html.haml_spec.rb @@ -13,8 +13,8 @@ describe "plantings/new" do assign(:planting, FactoryBot.create(:planting, garden: @garden_a, - crop: @crop2, - owner: @member)) + crop: @crop2, + owner: @member)) end context "logged in" do diff --git a/spec/views/posts/edit.html.haml_spec.rb b/spec/views/posts/edit.html.haml_spec.rb index 48d64a267..8ec056aff 100644 --- a/spec/views/posts/edit.html.haml_spec.rb +++ b/spec/views/posts/edit.html.haml_spec.rb @@ -32,7 +32,7 @@ describe "posts/edit" do before(:each) do @forum = assign(:forum, FactoryBot.create(:forum)) assign(:post, FactoryBot.create(:post, - forum: @forum, + forum: @forum, author: @author)) render end diff --git a/spec/views/roles/edit.html.haml_spec.rb b/spec/views/roles/edit.html.haml_spec.rb index a86f67ceb..01def5033 100644 --- a/spec/views/roles/edit.html.haml_spec.rb +++ b/spec/views/roles/edit.html.haml_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' describe "roles/edit" do before(:each) do @role = assign(:role, stub_model(Role, - name: "MyString", + name: "MyString", description: "MyText")) end diff --git a/spec/views/roles/index.html.haml_spec.rb b/spec/views/roles/index.html.haml_spec.rb index e4b28e9ba..8338f3cd9 100644 --- a/spec/views/roles/index.html.haml_spec.rb +++ b/spec/views/roles/index.html.haml_spec.rb @@ -5,10 +5,10 @@ describe "roles/index" do controller.stub(:current_user) { nil } assign(:roles, [ stub_model(Role, - name: "Name", + name: "Name", description: "MyText"), stub_model(Role, - name: "Name", + name: "Name", description: "MyText") ]) end diff --git a/spec/views/roles/new.html.haml_spec.rb b/spec/views/roles/new.html.haml_spec.rb index 1cdce8c41..eab80fa36 100644 --- a/spec/views/roles/new.html.haml_spec.rb +++ b/spec/views/roles/new.html.haml_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' describe "roles/new" do before(:each) do assign(:role, stub_model(Role, - name: "MyString", + name: "MyString", description: "MyText").as_new_record) end diff --git a/spec/views/roles/show.html.haml_spec.rb b/spec/views/roles/show.html.haml_spec.rb index 56f1cc4fb..f6d0b10b3 100644 --- a/spec/views/roles/show.html.haml_spec.rb +++ b/spec/views/roles/show.html.haml_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' describe "roles/show" do before(:each) do @role = assign(:role, stub_model(Role, - name: "Name", + name: "Name", description: "MyText")) end From 468dded162576fbe0fc1e1fd73e2f3886f2cc9f5 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:32:59 +1300 Subject: [PATCH 207/219] Remove controller actions for method that don't exist --- app/controllers/alternate_names_controller.rb | 2 +- app/controllers/members_controller.rb | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/controllers/alternate_names_controller.rb b/app/controllers/alternate_names_controller.rb index 0f1471a8c..3466f72a9 100644 --- a/app/controllers/alternate_names_controller.rb +++ b/app/controllers/alternate_names_controller.rb @@ -1,5 +1,5 @@ class AlternateNamesController < ApplicationController - before_action :authenticate_member!, except: %i(index show) + before_action :authenticate_member!, except: %i(index) load_and_authorize_resource respond_to :html, :json responders :flash diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb index 9576d6750..5ff82ab9f 100644 --- a/app/controllers/members_controller.rb +++ b/app/controllers/members_controller.rb @@ -2,7 +2,6 @@ class MembersController < ApplicationController load_and_authorize_resource except: %i(finish_signup unsubscribe view_follows view_followers show) skip_authorize_resource only: %i(nearby unsubscribe finish_signup) respond_to :html, :json, :rss - after_action :expire_homepage, only: :create def index @sort = params[:sort] From 98e492ad11d3e7627c9b868993aaa64071882730 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:33:10 +1300 Subject: [PATCH 208/219] DRY --- app/controllers/follows_controller.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/controllers/follows_controller.rb b/app/controllers/follows_controller.rb index b97eb9e2b..04853bb20 100644 --- a/app/controllers/follows_controller.rb +++ b/app/controllers/follows_controller.rb @@ -9,11 +9,10 @@ class FollowsController < ApplicationController if @follow.save flash[:notice] = "Followed #{@follow.followed.login_name}" - redirect_back fallback_location: root_path else flash[:error] = "Already following or error while following." - redirect_back fallback_location: root_path end + redirect_back fallback_location: root_path end # DELETE /follows/1 From 0083d1279afa6b53b73ba203817eb0d88d7edcfe Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:33:26 +1300 Subject: [PATCH 209/219] Marked htmlsafe as # rubocob:ignore Rails/OutputSafety --- app/helpers/application_helper.rb | 2 +- app/helpers/auto_suggest_helper.rb | 2 +- app/helpers/gardens_helper.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 9e65fc303..7ea7e26c2 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -29,7 +29,7 @@ module ApplicationHelper def required_field_help_text asterisk = content_tag :span, '*', class: ['red'] text = content_tag :em, 'denotes a required field' - content_tag :div, asterisk + ' '.html_safe + text, class: ['margin-bottom'] + content_tag :div, asterisk + ' '.html_safe + text, class: ['margin-bottom'] # rubocob:ignore Rails/OutputSafety end # diff --git a/app/helpers/auto_suggest_helper.rb b/app/helpers/auto_suggest_helper.rb index caa0cd7ff..cd410d86c 100644 --- a/app/helpers/auto_suggest_helper.rb +++ b/app/helpers/auto_suggest_helper.rb @@ -20,6 +20,6 @@ module AutoSuggestHelper - ).html_safe + ).html_safe # rubocob:ignore Rails/OutputSafety end end diff --git a/app/helpers/gardens_helper.rb b/app/helpers/gardens_helper.rb index a72505558..c1fdacb23 100644 --- a/app/helpers/gardens_helper.rb +++ b/app/helpers/gardens_helper.rb @@ -29,7 +29,7 @@ module GardensHelper output += ", planted on #{planting.planted_at}" end output += '' - output.html_safe + output.html_safe # rubocob:ignore Rails/OutputSafety end end end From b8fae99bde8a7482626abbea6c62c4887ceb762f Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:34:24 +1300 Subject: [PATCH 210/219] Times with zone, in factories --- spec/factories/member.rb | 2 +- spec/factories/post.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/factories/member.rb b/spec/factories/member.rb index fcd72c05e..21eaf99f5 100644 --- a/spec/factories/member.rb +++ b/spec/factories/member.rb @@ -4,7 +4,7 @@ FactoryBot.define do password { 'password1' } email { Faker::Internet.unique.email } tos_agreement { true } - confirmed_at { Time.now } + confirmed_at { Time.zone.now } show_email { false } bio { 'I love seeds' } diff --git a/spec/factories/post.rb b/spec/factories/post.rb index f88a123be..8284ed393 100644 --- a/spec/factories/post.rb +++ b/spec/factories/post.rb @@ -4,7 +4,7 @@ FactoryBot.define do body { "This is some text." } author - created_at { Time.now } + created_at { Time.zone.now } # Markdown is allowed in posts factory :markdown_post do From b1d2c46cff8440f615efacd6bfd72e87be948334 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:34:31 +1300 Subject: [PATCH 211/219] Whitespace fixup --- app/helpers/auto_suggest_helper.rb | 2 +- app/helpers/gardens_helper.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/auto_suggest_helper.rb b/app/helpers/auto_suggest_helper.rb index cd410d86c..83ff0fe08 100644 --- a/app/helpers/auto_suggest_helper.rb +++ b/app/helpers/auto_suggest_helper.rb @@ -20,6 +20,6 @@ module AutoSuggestHelper - ).html_safe # rubocob:ignore Rails/OutputSafety + ).html_safe # rubocob:ignore Rails/OutputSafety end end diff --git a/app/helpers/gardens_helper.rb b/app/helpers/gardens_helper.rb index c1fdacb23..229d2bb84 100644 --- a/app/helpers/gardens_helper.rb +++ b/app/helpers/gardens_helper.rb @@ -29,7 +29,7 @@ module GardensHelper output += ", planted on #{planting.planted_at}" end output += '' - output.html_safe # rubocob:ignore Rails/OutputSafety + output.html_safe # rubocob:ignore Rails/OutputSafety end end end From 93e1c5e5fc42c78f54241c5f6e3ddc24fe238ebb Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:38:37 +1300 Subject: [PATCH 212/219] Disabled # rubocop:disable Rails/OutputSafety --- app/helpers/application_helper.rb | 4 +++- app/helpers/auto_suggest_helper.rb | 4 +++- app/helpers/gardens_helper.rb | 4 +++- config.rb | 1 + 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 7ea7e26c2..f9d821fc6 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -27,9 +27,11 @@ module ApplicationHelper end def required_field_help_text + # rubocop:disable Rails/OutputSafety asterisk = content_tag :span, '*', class: ['red'] text = content_tag :em, 'denotes a required field' - content_tag :div, asterisk + ' '.html_safe + text, class: ['margin-bottom'] # rubocob:ignore Rails/OutputSafety + content_tag :div, asterisk + ' '.html_safe + text, class: ['margin-bottom'] + # rubocop:enable Rails/OutputSafety end # diff --git a/app/helpers/auto_suggest_helper.rb b/app/helpers/auto_suggest_helper.rb index 83ff0fe08..efa2e9ab6 100644 --- a/app/helpers/auto_suggest_helper.rb +++ b/app/helpers/auto_suggest_helper.rb @@ -1,4 +1,5 @@ module AutoSuggestHelper + # rubocop:disable Rails/OutputSafety def auto_suggest(resource, source, options = {}) if options[:default] && !options[:default].new_record? default = options[:default] @@ -20,6 +21,7 @@ module AutoSuggestHelper - ).html_safe # rubocob:ignore Rails/OutputSafety + ).html_safe end + # rubocop:enable Rails/OutputSafety end diff --git a/app/helpers/gardens_helper.rb b/app/helpers/gardens_helper.rb index 229d2bb84..fd406431e 100644 --- a/app/helpers/gardens_helper.rb +++ b/app/helpers/gardens_helper.rb @@ -21,6 +21,7 @@ module GardensHelper if plantings.blank? "None" else + # rubocop:disable Rails/OutputSafety output = '

    ' plantings.each do |planting| output += "
  • " @@ -29,7 +30,8 @@ module GardensHelper output += ", planted on #{planting.planted_at}
  • " end output += '
' - output.html_safe # rubocob:ignore Rails/OutputSafety + output.html_safe + # rubocop:enable Rails/OutputSafety end end end diff --git a/config.rb b/config.rb index f816551c1..acd8ec0eb 100644 --- a/config.rb +++ b/config.rb @@ -21,3 +21,4 @@ images_dir = "app/assets/images" preferred_syntax = :sass # and then run: # sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass +# rubocop:enable Lint/UselessAssignment From 3f5c63eb04c527fb2ad3050cb379d544cc230922 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:39:06 +1300 Subject: [PATCH 213/219] URI.escape -> CGI.escape --- app/helpers/crops_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/crops_helper.rb b/app/helpers/crops_helper.rb index b99d687aa..86b83909f 100644 --- a/app/helpers/crops_helper.rb +++ b/app/helpers/crops_helper.rb @@ -18,6 +18,6 @@ module CropsHelper end def crop_ebay_seeds_url(crop) - "http://rover.ebay.com/rover/1/705-53470-19255-0/1?icep_ff3=9&pub=5575213277&toolid=10001&campid=5337940151&customid=&icep_uq=#{URI.escape crop.name}&icep_sellerId=&icep_ex_kw=&icep_sortBy=12&icep_catId=181003&icep_minPrice=&icep_maxPrice=&ipn=psmain&icep_vectorid=229515&kwid=902099&mtid=824&kw=lg" # rubocop:disable Metrics/LineLength + "http://rover.ebay.com/rover/1/705-53470-19255-0/1?icep_ff3=9&pub=5575213277&toolid=10001&campid=5337940151&customid=&icep_uq=#{CGI.escape crop.name}&icep_sellerId=&icep_ex_kw=&icep_sortBy=12&icep_catId=181003&icep_minPrice=&icep_maxPrice=&ipn=psmain&icep_vectorid=229515&kwid=902099&mtid=824&kw=lg" # rubocop:disable Metrics/LineLength end end From 2faa2fd6f0b71fe971a7f14a270b710c050a2a73 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:42:51 +1300 Subject: [PATCH 214/219] Content for other formats on photos#show --- app/controllers/photos_controller.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb index 5a9f0129b..b7f9e16b1 100644 --- a/app/controllers/photos_controller.rb +++ b/app/controllers/photos_controller.rb @@ -5,7 +5,9 @@ class PhotosController < ApplicationController respond_to :html, :json responders :flash - def show; end + def show + respond_with(@photo) + end def index if params[:crop_id] From 60f72652da36d88c2229278c601d6fdd3fa56e92 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 15:53:52 +1300 Subject: [PATCH 215/219] URI.escape -> CGI.escape --- app/views/crops/show.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/crops/show.html.haml b/app/views/crops/show.html.haml index 7700501fa..940cc814e 100644 --- a/app/views/crops/show.html.haml +++ b/app/views/crops/show.html.haml @@ -112,16 +112,16 @@ %ul %li= link_to 'Wikipedia (English)', @crop.en_wikipedia_url, target: "_blank", rel: "noopener noreferrer" %li - = link_to "OpenFarm - Growing guide", "https://openfarm.cc/en/crops/#{URI.escape @crop.name}", + = link_to "OpenFarm - Growing guide", "https://openfarm.cc/en/crops/#{CGI.escape @crop.name}", target: "_blank", rel: "noopener noreferrer" %li - = link_to "Gardenate - Planting reminders", "http://www.gardenate.com/plant/#{URI.escape @crop.name}", + = link_to "Gardenate - Planting reminders", "http://www.gardenate.com/plant/#{CGI.escape @crop.name}", target: "_blank", rel: "noopener noreferrer" - if current_member && current_member.location %li = link_to "Google", - 'http://www.google.com/search?q=' + URI.escape(['Growing', + 'http://www.google.com/search?q=' + CGI.escape(['Growing', @crop.name, current_member.location].join(' ')), target: "_blank", From c0f8c2397641b08d1ef33309115be9ed3fca6b21 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 16:00:45 +1300 Subject: [PATCH 216/219] Downgrade geocoder --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 310b741bf..77af33e6a 100644 --- a/Gemfile +++ b/Gemfile @@ -67,7 +67,7 @@ gem 'friendly_id' gem 'gravatar-ultimate' # For geolocation -gem 'geocoder' +gem 'geocoder', '1.4.9' # TODO: Fails on version 1.5.0. Needs investigation # For easy calendar selection gem 'bootstrap-datepicker-rails' diff --git a/Gemfile.lock b/Gemfile.lock index 062eadd71..4fb2d4129 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -185,7 +185,7 @@ GEM sassc (>= 1.11) friendly_id (5.2.4) activerecord (>= 4.0.0) - geocoder (1.5.0) + geocoder (1.4.9) gibbon (1.2.1) httparty multi_json (>= 1.9.0) @@ -554,7 +554,7 @@ DEPENDENCIES flickraw font-awesome-sass friendly_id - geocoder + geocoder (= 1.4.9) gibbon (~> 1.2.0) gravatar-ultimate haml From ac660e986c36862b78cef850dddc0ad417e0c60b Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 16:14:00 +1300 Subject: [PATCH 217/219] Wrap all action bar button groups in a div So we can find them in specs --- app/views/crops/_actions.html.haml | 13 +++++++------ app/views/harvests/_actions.html.haml | 13 +++++++------ app/views/photos/_actions.html.haml | 13 +++++++------ app/views/seeds/_actions.html.haml | 24 ++++++++++++------------ spec/features/photos/new_photo_spec.rb | 4 ++-- 5 files changed, 35 insertions(+), 32 deletions(-) diff --git a/app/views/crops/_actions.html.haml b/app/views/crops/_actions.html.haml index 26f083d7b..e15979f61 100644 --- a/app/views/crops/_actions.html.haml +++ b/app/views/crops/_actions.html.haml @@ -1,8 +1,9 @@ -- if can? :create, Planting - = link_to "Plant this", new_planting_path(crop_id: crop.id), class: 'btn btn-default' +.crop-actions + - if can? :create, Planting + = link_to "Plant this", new_planting_path(crop_id: crop.id), class: 'btn btn-default' -- if can? :create, Harvest - = link_to "Harvest this", new_harvest_path(crop_id: crop.id), class: 'btn btn-default' + - if can? :create, Harvest + = link_to "Harvest this", new_harvest_path(crop_id: crop.id), class: 'btn btn-default' -- if can? :create, Seed - = link_to 'Add seeds to stash', new_seed_path(params: { crop_id: crop.id }), class: 'btn btn-default' + - if can? :create, Seed + = link_to 'Add seeds to stash', new_seed_path(params: { crop_id: crop.id }), class: 'btn btn-default' diff --git a/app/views/harvests/_actions.html.haml b/app/views/harvests/_actions.html.haml index 5a2f1febc..2ebfbc8f9 100644 --- a/app/views/harvests/_actions.html.haml +++ b/app/views/harvests/_actions.html.haml @@ -1,6 +1,7 @@ -- if can?(:edit, harvest) || can?(:destroy, harvest) - .btn-group.harvest-actions - - if can? :edit, harvest - = render 'shared/buttons/edit', path: edit_harvest_path(harvest) - - if can? :destroy, harvest - .pull-right= render 'shared/buttons/delete', path: harvest_path(harvest) +.harvest-actions + - if can?(:edit, harvest) || can?(:destroy, harvest) + .btn-group + - if can? :edit, harvest + = render 'shared/buttons/edit', path: edit_harvest_path(harvest) + - if can? :destroy, harvest + .pull-right= render 'shared/buttons/delete', path: harvest_path(harvest) diff --git a/app/views/photos/_actions.html.haml b/app/views/photos/_actions.html.haml index 878d985ef..06fc37f0e 100644 --- a/app/views/photos/_actions.html.haml +++ b/app/views/photos/_actions.html.haml @@ -1,6 +1,7 @@ -- if can?(:edit, @photo) && can?(:destroy, @photo) - %p.photo-actions - - if can?(:edit, @photo) - = render 'shared/buttons/edit', path: edit_photo_path(@photo) - - if can?(:destroy, @photo) - = render 'shared/buttons/delete', path: photo_path(@photo) +.photo-actions + - if can?(:edit, @photo) && can?(:destroy, @photo) + %p + - if can?(:edit, @photo) + = render 'shared/buttons/edit', path: edit_photo_path(@photo) + - if can?(:destroy, @photo) + = render 'shared/buttons/delete', path: photo_path(@photo) diff --git a/app/views/seeds/_actions.html.haml b/app/views/seeds/_actions.html.haml index 803b63b2d..6e1cef334 100644 --- a/app/views/seeds/_actions.html.haml +++ b/app/views/seeds/_actions.html.haml @@ -1,15 +1,15 @@ +.seed-actions + - if can? :edit, seed + .btn-group + = render 'shared/buttons/edit', path: edit_seed_path(seed) + = render 'shared/buttons/add_photo', path: new_photo_path(id: seed.id, type: 'seed') -- if can? :edit, seed - .btn-group - = render 'shared/buttons/edit', path: edit_seed_path(seed) - = render 'shared/buttons/add_photo', path: new_photo_path(id: seed.id, type: 'seed') + - if can?(:create, Planting) && seed.active? + = link_to new_planting_path(seed_id: seed), class: 'btn btn-default btn-xs' do + %span.glyphicon.glyphicon-grain{ title: "Plant seeds" } + Plant seeds - - if can?(:create, Planting) && seed.active? - = link_to new_planting_path(seed_id: seed), class: 'btn btn-default btn-xs' do - %span.glyphicon.glyphicon-grain{ title: "Plant seeds" } - Plant seeds + = render 'shared/buttons/finish_seeds', seed: seed - = render 'shared/buttons/finish_seeds', seed: seed - -- if can? :destroy, seed - = render 'shared/buttons/delete', path: seed + - if can? :destroy, seed + = render 'shared/buttons/delete', path: seed diff --git a/spec/features/photos/new_photo_spec.rb b/spec/features/photos/new_photo_spec.rb index ee2a50691..0bf696f06 100644 --- a/spec/features/photos/new_photo_spec.rb +++ b/spec/features/photos/new_photo_spec.rb @@ -40,12 +40,12 @@ feature "new photo page" do end end - pending "viewing a seed" do + describe "viewing a seed" do let(:seed) { FactoryBot.create :seed, owner: member } scenario "add photo" do visit seed_path(seed) - click_link "Add photo" + first('.seed-actions').click_link('Add photo') expect(page).to have_text seed.to_s end end From b70dee50412ff4930cb918256f1a54310b11aa00 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 19:37:28 +1300 Subject: [PATCH 218/219] Update rubocop todo --- .rubocop_todo.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index eb05ad7ff..784aaf01f 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --no-offense-counts --no-auto-gen-timestamp` -# using RuboCop version 0.55.0. +# using RuboCop version 0.61.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -30,6 +30,8 @@ Rails/CreateTableWithTimestamps: - 'db/migrate/20161201154922_add_photos_seeds_table.rb' - 'db/migrate/20171022032108_all_the_predictions.rb' +# Configuration parameters: EnforcedStyle. +# SupportedStyles: slashes, arguments Rails/FilePath: Exclude: - 'spec/rails_helper.rb' @@ -40,7 +42,7 @@ Rails/HasManyOrHasOneDependent: Exclude: - 'app/models/member.rb' -# Configuration parameters: Blacklist. +# Configuration parameters: Blacklist, Whitelist. # Blacklist: decrement!, decrement_counter, increment!, increment_counter, toggle!, touch, update_all, update_attribute, update_column, update_columns, update_counters Rails/SkipsModelValidations: Exclude: @@ -68,7 +70,7 @@ Style/MixinUsage: - 'spec/rails_helper.rb' # Cop supports --auto-correct. -# Configuration parameters: AutoCorrect, EnforcedStyle. +# Configuration parameters: AutoCorrect, EnforcedStyle, IgnoredMethods. # SupportedStyles: predicate, comparison Style/NumericPredicate: Exclude: From f2fc6b2a61ed2dcd09d07ff1c9dc32a11e732425 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 30 Dec 2018 19:41:08 +1300 Subject: [PATCH 219/219] Reduce max class complexity --- .rubocop.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index 7c61c6fd1..43e24f9c4 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -63,8 +63,9 @@ Metrics/MethodLength: Max: 34 Metrics/AbcSize: Max: 30 +# Configuration parameters: CountComments. Metrics/ClassLength: - Max: 172 + Max: 171 Metrics/CyclomaticComplexity: Max: 7 Metrics/PerceivedComplexity: