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/.overcommit.yml b/.overcommit.yml index 87c26ceeb..a9ee0ed8c 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -60,6 +60,7 @@ PreCommit: - 'app/assets/**' - 'spec/javascripts/support/vendor/**' - '**/bootstrap*' + - 'app/assets/javascripts/highcharts.js' command: ['./node_modules/.bin/eslint'] required_executable: 'npm' ScssLint: diff --git a/.rubocop.yml b/.rubocop.yml index 545e1c1fa..43e24f9c4 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,18 +1,14 @@ 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 -Style/FileName: +Naming/FileName: Exclude: - 'Guardfile' - 'Gemfile' @@ -21,7 +17,6 @@ Style/FileName: Style/StringLiterals: Enabled: false -# Stop hound and codeclimate fighting Style/PercentLiteralDelimiters: PreferredDelimiters: default: () @@ -31,17 +26,15 @@ 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: EnforcedStyle: with_fixed_indentation -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 @@ -62,14 +55,18 @@ Metrics/BlockLength: - '**/*.rake' - 'config/**/*.rb' +Metrics/LineLength: + Max: 120 + # Remove the following once the code style matches Metrics/MethodLength: Max: 34 Metrics/AbcSize: - Max: 31 + Max: 30 +# Configuration parameters: CountComments. Metrics/ClassLength: - Max: 179 + Max: 171 Metrics/CyclomaticComplexity: - Max: 11 + Max: 7 Metrics/PerceivedComplexity: - Max: 9 + Max: 8 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 769bcb9b6..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` -# on 2018-02-05 14:37:22 +1300 using RuboCop version 0.49.1. +# `rubocop --auto-gen-config --no-offense-counts --no-auto-gen-timestamp` +# 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 @@ -10,69 +10,67 @@ Lint/HandleExceptions: Exclude: - 'lib/tasks/testing.rake' -# Cop supports --auto-correct. -# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods. -Lint/UnusedMethodArgument: +# Configuration parameters: EnforcedStyle. +# SupportedStyles: lowercase, uppercase +Naming/HeredocDelimiterCase: Exclude: - - 'app/controllers/application_controller.rb' - - 'app/controllers/passwords_controller.rb' - - 'app/controllers/registrations_controller.rb' - - 'app/validators/approved_validator.rb' + - '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' + +# Configuration parameters: EnforcedStyle. +# SupportedStyles: slashes, arguments Rails/FilePath: Exclude: - 'spec/rails_helper.rb' -Rails/OutputSafety: +# Configuration parameters: Include. +# Include: app/models/**/*.rb +Rails/HasManyOrHasOneDependent: Exclude: - - 'app/helpers/application_helper.rb' - - 'app/helpers/auto_suggest_helper.rb' - - 'app/helpers/gardens_helper.rb' + - '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: - 'db/seeds.rb' -# Configuration parameters: EnforcedStyle, SupportedStyles. -# 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: EnforcedStyle, SupportedStyles. -# SupportedStyles: always, conditionals -Style/AndOr: - Exclude: - - 'config/unicorn.rb' - - 'lib/tasks/growstuff.rake' - -Style/AsciiComments: - Exclude: - - 'config/initializers/comfortable_mexican_sofa.rb' - -# Configuration parameters: EnforcedStyle, SupportedStyles. +# Configuration parameters: AutoCorrect, EnforcedStyle. # SupportedStyles: nested, compact Style/ClassAndModuleChildren: Exclude: - 'lib/actions/oauth_signup_action.rb' - 'lib/haml/filters/escaped_markdown.rb' -Style/IdenticalConditionalBranches: +Style/CommentedKeyword: Exclude: - - 'app/controllers/follows_controller.rb' + - 'lib/tasks/growstuff.rake' + - 'spec/models/crop_spec.rb' + - 'spec/models/photo_spec.rb' + - 'spec/models/planting_spec.rb' -# Cop supports --auto-correct. -Style/MultilineIfModifier: +Style/MixinUsage: Exclude: + - 'bin/setup' + - 'bin/update' - 'spec/rails_helper.rb' # Cop supports --auto-correct. -# Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles. +# Configuration parameters: AutoCorrect, EnforcedStyle, IgnoredMethods. # SupportedStyles: predicate, comparison Style/NumericPredicate: Exclude: @@ -80,14 +78,3 @@ Style/NumericPredicate: - 'app/helpers/harvests_helper.rb' - 'app/helpers/plantings_helper.rb' - 'lib/tasks/growstuff.rake' - -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, 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' 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` diff --git a/Gemfile b/Gemfile index fe88dd742..77af33e6a 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,11 +22,12 @@ 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' -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 @@ -42,7 +43,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 @@ -66,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' @@ -82,13 +83,12 @@ 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 -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' @@ -97,10 +97,12 @@ 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 +gem 'puma' + group :production, :staging do gem 'bonsai-elasticsearch-rails' # Integration with Bonsa-Elasticsearch on heroku gem 'dalli' @@ -111,16 +113,9 @@ 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' end group :development, :test do @@ -135,19 +130,20 @@ 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.60' + gem 'rubocop-rspec' gem 'selenium-webdriver' gem 'webrat' # provides HTML matchers for view tests 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 4bf828c6f..b7d1d6d19 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,89 +1,112 @@ +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/ specs: - actionmailer (4.2.11) - actionpack (= 4.2.11) - actionview (= 4.2.11) - activejob (= 4.2.11) + 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.11) - actionview (= 4.2.11) - activesupport (= 4.2.11) - 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.11) - activesupport (= 4.2.11) + 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_utils (3.3.15) + active_utils (3.3.16) activesupport (>= 4.2) i18n - activejob (4.2.11) - activesupport (= 4.2.11) - globalid (>= 0.3.0) - activemodel (4.2.11) - activesupport (= 4.2.11) - builder (~> 3.1) - activerecord (4.2.11) - activemodel (= 4.2.11) - activesupport (= 4.2.11) - arel (~> 6.0) - activesupport (4.2.11) + activejob (5.1.4) + activesupport (= 5.1.4) + globalid (>= 0.3.6) + 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.4.0) - autoprefixer-rails (8.6.4) + autoprefixer-rails (9.4.3) execjs bcrypt (3.1.12) - better_errors (2.2.0) + better_errors (2.5.0) coderay (>= 1.0.0) - erubis (>= 2.6.6) + erubi (>= 1.0.0) rack (>= 0.9.0) - binding_of_caller (0.8.0) - debug_inspector (>= 0.0.1) 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) 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.3.0) - capybara (2.18.0) + capybara (3.12.0) 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) + regexp_parser (~> 1.2) + xpath (~> 3.2) capybara-email (3.0.1) capybara (>= 2.4, < 4.0) mail @@ -107,21 +130,6 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - comfortable_mexican_sofa (1.12.11) - 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 (>= 4.0.0, < 5.1) - rails-i18n (>= 4.0.0) - sass-rails (>= 4.0.3) concurrent-ruby (1.1.4) connection_pool (2.2.2) coveralls (0.8.19) @@ -135,7 +143,6 @@ GEM activesupport (>= 3.0.0) dalli (2.7.9) database_cleaner (1.7.0) - debug_inspector (0.0.3) devise (4.5.0) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -144,22 +151,20 @@ 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) - 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) erubis (2.7.0) excon (0.62.0) execjs (2.7.0) @@ -170,15 +175,14 @@ 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.5.0.1) + font-awesome-sass (5.6.1) sassc (>= 1.11) - formatador (0.2.5) friendly_id (5.2.5) activerecord (>= 4.0.0) geocoder (1.4.9) @@ -190,20 +194,6 @@ GEM gravatar-ultimate (2.0.0) activesupport (>= 2.3.14) rack - guard (2.15.0) - formatador (>= 0.2.4) - listen (>= 2.7, < 4.0) - lumberjack (>= 1.0.12, < 2.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 @@ -219,45 +209,47 @@ GEM haml (>= 4.0.6, < 6.0) html2haml (>= 1.0.1) railties (>= 4.0.1) - haml_lint (0.26.0) + haml_lint (0.28.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) + hashie (3.6.0) heroics (0.0.25) erubis (~> 2.0) 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) 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.12) + i18n-tasks (0.9.28) activesupport (>= 4.0.2) ast (>= 2.1.0) - easy_translate (>= 0.5.0) - erubis - highline (>= 1.7.3) + erubi + highline (>= 2.0.0) i18n parser (>= 2.2.3.0) - term-ansicolor (>= 1.3.2) + rails-i18n + 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 (5.0.5) + 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) @@ -265,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) @@ -293,38 +285,34 @@ GEM loofah (2.2.3) crass (~> 1.0.2) nokogiri (>= 1.5.9) - lumberjack (1.0.13) mail (2.7.1) mini_mime (>= 0.1.1) memcachier (0.0.2) method_source (0.9.2) - mime-types (3.1) + mime-types (3.2.2) mime-types-data (~> 3.2015) - mime-types-data (3.2016.0521) - mimemagic (0.3.2) + mime-types-data (3.2018.0812) + 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) - nenv (0.3.0) newrelic_rpm (5.6.0.349) - nokogiri (1.8.5) - mini_portile2 (~> 2.3.0) - notiffany (0.1.1) - nenv (~> 0.1) - shellany (~> 0.0) + nio4r (2.3.1) + 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) @@ -334,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 @@ -348,7 +336,9 @@ GEM mimemagic (~> 0.3.0) terrapin (~> 0.6.0) parallel (1.12.1) - parser (2.5.1.0) + paranoia (2.4.1) + activerecord (>= 4.0, < 5.3) + parser (2.5.3.0) ast (~> 2.4.0) pg (0.21.0) platform-api (2.2.0) @@ -356,71 +346,67 @@ GEM moneta (~> 1.0.0) 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) - pry (0.12.2) - coderay (~> 1.1.0) - method_source (~> 0.9.0) + powerpack (0.1.2) public_suffix (3.0.3) - quiet_assets (1.1.0) - railties (>= 3.1, < 5.0) - rack (1.6.11) - rack-protection (2.0.4) + puma (3.12.0) + rack (2.0.6) + rack-protection (2.0.5) rack - rack-test (0.6.3) - rack (>= 1.0) - rails (4.2.11) - actionmailer (= 4.2.11) - actionpack (= 4.2.11) - actionview (= 4.2.11) - activejob (= 4.2.11) - activemodel (= 4.2.11) - activerecord (= 4.2.11) - activesupport (= 4.2.11) - bundler (>= 1.3.0, < 2.0) - railties (= 4.2.11) - sprockets-rails + rack-test (1.1.0) + 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.3.4) rails-assets-leaflet.markercluster (1.4.1) rails-assets-leaflet (>= 1.3.1) - 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-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 (4.0.9) - i18n (~> 0.7) - railties (~> 4.0) + rails-i18n (5.1.2) + i18n (>= 0.7, < 2) + railties (>= 5.0, < 6) 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.11) - actionpack (= 4.2.11) - activesupport (= 4.2.11) + 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) + 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 (3.8.0) - rspec-core (~> 3.8.0) - rspec-expectations (~> 3.8.0) - rspec-mocks (~> 3.8.0) rspec-activemodel-mocks (1.1.0) activemodel (>= 3.0) activesupport (>= 3.0) @@ -442,20 +428,23 @@ GEM rspec-mocks (~> 3.8.0) rspec-support (~> 3.8.0) rspec-support (3.8.0) - rubocop (0.49.1) + rubocop (0.61.1) + jaro_winkler (~> 1.5.1) parallel (~> 1.10) - parser (>= 2.3.3.1, < 3.0) + parser (>= 2.5, != 2.5.1.1) 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) + unicode-display_width (~> 1.4.0) + rubocop-rspec (1.30.1) + rubocop (>= 0.60.0) + ruby-progressbar (1.10.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.6.0) + sass (3.7.2) sass-listen (~> 4.0.0) sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) @@ -473,8 +462,7 @@ GEM childprocess (~> 0.5) rubyzip (~> 1.2, >= 1.2.2) sexp_processor (4.11.0) - shellany (0.0.1) - sidekiq (5.2.2) + sidekiq (5.2.3) connection_pool (~> 2.2, >= 2.2.2) rack-protection (>= 1.5.0) redis (>= 3.3.5, < 5) @@ -494,35 +482,34 @@ 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) 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) + 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.15) + uglifier (4.1.20) execjs (>= 0.3.0, < 3) - unicode-display_width (1.3.2) + 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) 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) @@ -534,11 +521,9 @@ PLATFORMS ruby DEPENDENCIES - active_median + active_median (= 0.1.4) active_utils - acts_as_paranoid (~> 0.5.0) - better_errors (~> 2.2.0) - binding_of_caller + better_errors bluecloth bonsai-elasticsearch-rails bootstrap-datepicker-rails @@ -554,39 +539,38 @@ DEPENDENCIES chartkick codeclimate-test-reporter coffee-rails - comfortable_mexican_sofa + comfortable_mexican_sofa! coveralls csv_shaper 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 flickraw font-awesome-sass friendly_id - geocoder + geocoder (= 1.4.9) gibbon (~> 1.2.0) gravatar-ultimate - guard - guard-rspec haml haml-i18n-extractor haml-rails - haml_lint + haml_lint (>= 0.25.1) hashie (>= 3.5.3) i18n-tasks jquery-rails - jquery-ui-rails (~> 5.0.2) + jquery-ui-rails js-routes jsonapi-resources kaminari leaflet-rails letter_opener + listen loofah (>= 2.2.1) memcachier newrelic_rpm @@ -594,21 +578,22 @@ DEPENDENCIES omniauth-facebook omniauth-flickr (>= 0.0.15) omniauth-twitter + paranoia (~> 2.2) pg (< 1.0.0) platform-api poltergeist - pry - quiet_assets + puma rack-protection (>= 2.0.1) - rails (~> 4.2.8) + rails (= 5.1.4) 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.60) + rubocop-rspec ruby-units sass-rails selenium-webdriver 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/Rakefile b/Rakefile index 7e5e8eed3..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__) -Growstuff::Application.load_tasks +Rails.application.load_tasks diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index fc3369264..419c55d00 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 . 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/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"; + nominatim_user_agent_email = "<%= Rails.env == 'test' ? 0 : Rails.application.config.user_agent_email %>"; L.Icon.Default.imagePath = '/assets' diff --git a/app/assets/stylesheets/predictions.sass b/app/assets/stylesheets/predictions.sass index 00be9af21..ad7fd45c1 100644 --- a/app/assets/stylesheets/predictions.sass +++ b/app/assets/stylesheets/predictions.sass @@ -8,4 +8,3 @@ font-size: 250% font-align: center h3 - 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/application_controller.rb b/app/controllers/application_controller.rb index 3e6221d06..9ef6783e5 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,11 +19,15 @@ class ApplicationController < ActionController::Base end end - def after_sign_in_path_for(resource) + 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 - 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/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/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 diff --git a/app/controllers/crops_controller.rb b/app/controllers/crops_controller.rb index 361e28031..abfb47a0e 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? @@ -171,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/follows_controller.rb b/app/controllers/follows_controller.rb index 2c4b2daab..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_to :back else flash[:error] = "Already following or error while following." - redirect_to :back end + redirect_back fallback_location: root_path end # DELETE /follows/1 diff --git a/app/controllers/gardens_controller.rb b/app/controllers/gardens_controller.rb index ede5c2602..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 @@ -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 diff --git a/app/controllers/harvests_controller.rb b/app/controllers/harvests_controller.rb index f731b1018..6ea5b32ca 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 diff --git a/app/controllers/likes_controller.rb b/app/controllers/likes_controller.rb index 5f7c5dac9..e942d5b1f 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 @@ -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 @@ -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/members_controller.rb b/app/controllers/members_controller.rb index ae720e57b..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] @@ -51,7 +50,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/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/photo_associations_controller.rb b/app/controllers/photo_associations_controller.rb index 126aaf9b3..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) @@ -11,6 +12,8 @@ class PhotoAssociationsController < ApplicationController respond_with(@photo) end + private + def item_class params[:type].capitalize end diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb index 57b10e5d8..b7f9e16b1 100644 --- a/app/controllers/photos_controller.rb +++ b/app/controllers/photos_controller.rb @@ -1,10 +1,14 @@ 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 + def show + respond_with(@photo) + end + def index if params[:crop_id] @crop = Crop.find params[:crop_id] @@ -36,6 +40,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,9 +82,11 @@ 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_by!(id: params[:id], owner_id: current_member.id) + + item_class.constantize.find(params[:id]) end # diff --git a/app/controllers/plantings_controller.rb b/app/controllers/plantings_controller.rb index 5ee31ea2c..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 @@ -73,7 +73,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 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/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 e08df568d..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'] + # rubocop:enable Rails/OutputSafety end # @@ -39,14 +41,13 @@ 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 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 @@ -54,7 +55,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 @@ -69,16 +70,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) diff --git a/app/helpers/auto_suggest_helper.rb b/app/helpers/auto_suggest_helper.rb index caa0cd7ff..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] @@ -22,4 +23,5 @@ module AutoSuggestHelper type="hidden" name="#{resource}[#{source}_id]" value="#{default_id}"> ).html_safe end + # rubocop:enable Rails/OutputSafety end 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 diff --git a/app/helpers/gardens_helper.rb b/app/helpers/gardens_helper.rb index a72505558..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 += "
  • " @@ -30,6 +31,7 @@ module GardensHelper end output += '
' output.html_safe + # rubocop:enable Rails/OutputSafety end end end 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 4ea42f43e..2ebe7569f 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) @@ -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/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 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 85e025e3f..0d4ea407e 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 " @@ -19,7 +19,7 @@ class Notifier < ActionMailer::Base 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/alternate_name.rb b/app/models/alternate_name.rb index aa960e1f4..94e404b49 100644 --- a/app/models/alternate_name.rb +++ b/app/models/alternate_name.rb @@ -1,7 +1,7 @@ -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' + belongs_to :creator, class_name: 'Member', inverse_of: :created_alternate_names validates :name, presence: true validates :crop, presence: true end 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..182e1e61f 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,5 +1,5 @@ -class Comment < ActiveRecord::Base - belongs_to :author, class_name: 'Member' +class Comment < ApplicationRecord + 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 @@ -11,10 +11,10 @@ class Comment < ActiveRecord::Base 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/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 177a2f832..3ae7303d9 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 :photographings, as: :photographable, dependent: :destroy, inverse_of: :photographable has_many :photos, through: :photographings, as: :photographable - has_many :photographings, as: :photographable, dependent: :destroy scope :has_photos, -> { includes(:photos).where.not(photos: { id: nil }) } 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 9710952a0..2846b8a5f 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) @@ -15,11 +15,11 @@ class Crop < ActiveRecord::Base 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 - belongs_to :creator, class_name: 'Member' - belongs_to :requester, class_name: 'Member' - belongs_to :parent, class_name: 'Crop' - has_many :varieties, class_name: 'Crop', foreign_key: 'parent_id', dependent: :nullify + 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: :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 ## @@ -44,10 +44,10 @@ class Crop < ActiveRecord::Base ## 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 < ActiveRecord::Base # 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 < ActiveRecord::Base 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' @@ -156,6 +156,7 @@ class Crop < ActiveRecord::Base 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 < ActiveRecord::Base def rejection_explanation return rejection_notes if reason_for_rejection == "other" + reason_for_rejection end @@ -222,17 +224,20 @@ class Crop < ActiveRecord::Base 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 2f646b6ab..d228bb3e1 100644 --- a/app/models/csv_importer.rb +++ b/app/models/csv_importer.rb @@ -9,9 +9,9 @@ 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 + creator_id: cropbot.id ) add_parent(parent_name) if parent_name @@ -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 @@ -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/follow.rb b/app/models/follow.rb index 557887cb6..6562ca92c 100644 --- a/app/models/follow.rb +++ b/app/models/follow.rb @@ -1,14 +1,14 @@ -class Follow < ActiveRecord::Base - belongs_to :follower, class_name: "Member" - belongs_to :followed, class_name: "Member" +class Follow < ApplicationRecord + 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 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/forum.rb b/app/models/forum.rb index 34afad592..664af4f38 100644 --- a/app/models/forum.rb +++ b/app/models/forum.rb @@ -1,10 +1,10 @@ -class Forum < ActiveRecord::Base +class Forum < ApplicationRecord extend FriendlyId include Ownable validates :name, presence: true friendly_id :name, use: %i(slugged finders) - has_many :posts + has_many :posts, dependent: :destroy def to_s name diff --git a/app/models/garden.rb b/app/models/garden.rb index 682796674..880dda91e 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 @@ -29,26 +29,26 @@ class Garden < ActiveRecord::Base 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 def cleanup_area - self.area = nil if area && area.zero? + self.area = nil if area&.zero? self.area_unit = nil if area.blank? end @@ -62,7 +62,7 @@ class Garden < ActiveRecord::Base 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/harvest.rb b/app/models/harvest.rb index 2b0a5a3a1..94b71d7b4 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 @@ -9,15 +9,15 @@ class Harvest < ActiveRecord::Base # 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 = { @@ -35,7 +35,7 @@ class Harvest < ActiveRecord::Base ## Relationships belongs_to :crop belongs_to :plant_part - belongs_to :planting + belongs_to :planting, optional: true ## ## Scopes @@ -70,6 +70,7 @@ class Harvest < ActiveRecord::Base def time_from_planting_to_harvest return if planting.blank? + harvested_at - planting.planted_at end @@ -77,14 +78,15 @@ class Harvest < ActiveRecord::Base # 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 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 @@ -101,11 +103,13 @@ class Harvest < ActiveRecord::Base 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 < ActiveRecord::Base 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 < ActiveRecord::Base 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/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 e7601f43d..2247bd038 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 @@ -8,25 +8,38 @@ class Member < ActiveRecord::Base # # 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, 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 :authentications - has_many :photos - has_many :requested_crops, class_name: Crop, foreign_key: 'requester_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, inverse_of: :owner 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 + + # + # 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", + dependent: :destroy, inverse_of: :followed 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) } @@ -56,13 +69,13 @@ class Member < ActiveRecord::Base # 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: { @@ -85,6 +98,7 @@ class Member < ActiveRecord::Base conditions = warden_conditions.dup login = conditions.delete(:login) return where(conditions).login_name_or_email(login).first if login + find_by(conditions) end @@ -122,17 +136,18 @@ class Member < ActiveRecord::Base 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 return [result.photo, result.total] if result + [[], 0] end @@ -157,37 +172,45 @@ class Member < ActiveRecord::Base 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 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 newsletter_subscribe(gb = Gibbon::API.new, testing = false) + 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(gibbon = Gibbon::API.new, testing = false) return true if Rails.env.test? && !testing - gb.lists.subscribe( - id: Growstuff::Application.config.newsletter_list_id, - email: { email: email }, - merge_vars: { login_name: login_name }, + + gibbon.lists.subscribe( + 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 - 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: Growstuff::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/notification.rb b/app/models/notification.rb index 1b09b1e5e..d4e8db676 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -1,7 +1,7 @@ -class Notification < ActiveRecord::Base - belongs_to :sender, class_name: 'Member' - belongs_to :recipient, class_name: 'Member' - belongs_to :post +class Notification < ApplicationRecord + 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/photo.rb b/app/models/photo.rb index 75a35b10a..87e9f9f4c 100644 --- a/app/models/photo.rb +++ b/app/models/photo.rb @@ -1,14 +1,15 @@ -class Photo < ActiveRecord::Base +class Photo < ApplicationRecord include Ownable 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, - through: :photographings, - source: :photographable, + through: :photographings, + source: :photographable, source_type: type end @@ -22,13 +23,13 @@ class Photo < ActiveRecord::Base 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 @@ -51,7 +52,7 @@ class Photo < ActiveRecord::Base end def set_flickr_metadata! - update_attributes(flickr_metadata) + update(flickr_metadata) end def to_s diff --git a/app/models/photographing.rb b/app/models/photographing.rb index 3edb78351..b62ed10e6 100644 --- a/app/models/photographing.rb +++ b/app/models/photographing.rb @@ -1,7 +1,9 @@ -class Photographing < ActiveRecord::Base - belongs_to :photo +class Photographing < ApplicationRecord + belongs_to :photo, inverse_of: :photographings 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 < ActiveRecord::Base 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_id == photo.owner_id + end end diff --git a/app/models/plant_part.rb b/app/models/plant_part.rb index 15a68d977..7af288706 100644 --- a/app/models/plant_part.rb +++ b/app/models/plant_part.rb @@ -1,25 +1,13 @@ -class PlantPart < ActiveRecord::Base +class PlantPart < ApplicationRecord extend FriendlyId friendly_id :name, use: %i(slugged finders) - has_many :harvests - has_many :crops, -> { uniq }, through: :harvests + has_many :harvests, dependent: :destroy + has_many :crops, -> { distinct }, through: :harvests validates :name, presence: true, uniqueness: true 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 diff --git a/app/models/planting.rb b/app/models/planting.rb index bea6b9a8b..984b55c03 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 include Finishable @@ -21,14 +21,19 @@ class Planting < ActiveRecord::Base # # Ancestry of food - belongs_to :parent_seed, class_name: 'Seed', foreign_key: 'parent_seed_id' # 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 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) @@ -100,6 +105,7 @@ class Planting < ActiveRecord::Base # 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 43885e99d..46efa2e45 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -1,12 +1,12 @@ -class Post < ActiveRecord::Base +class Post < ApplicationRecord extend FriendlyId include Likeable friendly_id :author_date_subject, use: %i(slugged finders) # # Relationships - belongs_to :author, class_name: 'Member' - belongs_to :forum + 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 # also has_many notifications, but kinda meaningless to get at them @@ -77,11 +77,12 @@ class Post < ActiveRecord::Base # 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, - 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/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..7b20d2e9e 100644 --- a/app/models/scientific_name.rb +++ b/app/models/scientific_name.rb @@ -1,7 +1,7 @@ -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' + 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 e83747b77..2075afba2 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 include Finishable @@ -13,19 +13,21 @@ class Seed < ActiveRecord::Base # # Relationships belongs_to :crop - 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, inverse_of: :child_seeds # 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_seed # children # # 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/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/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/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/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/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/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", 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.

+
+ + 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/layouts/application.html.haml b/app/views/layouts/application.html.haml index 6b9cfd40f..070ed73db 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -26,4 +26,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/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 %> 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/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/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/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 } 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/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..b2293a35a --- /dev/null +++ b/bin/setup @@ -0,0 +1,37 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('..', __dir__) + +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..32326c74f --- /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('..', __dir__) + +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..4d2c50e52 --- /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 + 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.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 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/application.rb b/config/application.rb index ae8d6e95f..0a00a917a 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. @@ -102,6 +102,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/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/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/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/environments/development.rb b/config/environments/development.rb index a66a807f7..7b04cf96c 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,57 +1,66 @@ -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' } 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/production.rb b/config/environments/production.rb index 81cbddefd..9d377db43 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. @@ -92,4 +105,7 @@ Growstuff::Application.configure do config.mapbox_access_token = 'pk.eyJ1IjoiZ3Jvd3N0dWZmIiwiYSI6IkdxMkx4alUifQ.n0igaBsw97s14zMa0lwKCA' 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 d4b381577..aa2afd565 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 d3ad694dd..47fede3a8 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' @@ -58,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 } ] ) @@ -81,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 } ] ) @@ -90,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 } ] ) @@ -101,19 +98,20 @@ 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 # 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/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/comfortable_mexican_sofa.rb b/config/initializers/comfortable_mexican_sofa.rb index 78794d93c..43630ef0a 100644 --- a/config/initializers/comfortable_mexican_sofa.rb +++ b/config/initializers/comfortable_mexican_sofa.rb @@ -1,5 +1,3 @@ -# encoding: utf-8 - ComfortableMexicanSofa.configure do |config| # Title of the admin area # config.cms_title = 'ComfortableMexicanSofa CMS Engine' @@ -57,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` @@ -95,7 +95,8 @@ end module CmsDeviseAuth def authenticate - return if current_member && current_member.role?(:admin) + 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/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/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 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..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" => - "#{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 f30e7af4f..039431538 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..bffab6c85 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. +# To enable root element in JSON for ActiveRecord objects. ActiveSupport.on_load(:active_record) do self.include_root_in_json = false 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 52517f580..9f8918eed 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,12 +1,12 @@ -Growstuff::Application.routes.draw do +Rails.application.routes.draw do get '/robots.txt' => 'robots#robots' 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 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/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/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 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..71ba20b11 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' @@ -113,7 +113,7 @@ class CreateCms < ActiveRecord::Migration 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 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/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 5f5590d37..c1931b2b6 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 8054ba9cd..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 @@ -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 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..0e1332ab3 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) @@ -7,16 +7,16 @@ class SeedUsage < ActiveRecord::Migration # 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/migrate/20180401220637_add_member_count_caches.rb b/db/migrate/20180401220637_add_member_count_caches.rb index 151f19fcd..912db008f 100644 --- a/db/migrate/20180401220637_add_member_count_caches.rb +++ b/db/migrate/20180401220637_add_member_count_caches.rb @@ -1,4 +1,4 @@ -class AddMemberCountCaches < ActiveRecord::Migration +class AddMemberCountCaches < ActiveRecord::Migration[4.2] def change add_column :members, :photos_count, :integer add_column :members, :forums_count, :integer diff --git a/db/schema.rb b/db/schema.rb index 71338e4a2..6640ce490 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. @@ -16,331 +15,312 @@ ActiveRecord::Schema.define(version: 20180401220637) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" - 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.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" + 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.string "likeable_type" + t.integer "likeable_id" + 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.integer "photos_count" - t.integer "forums_count" + t.integer "photos_count" + t.integer "forums_count" + 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 @@ -350,28 +330,27 @@ ActiveRecord::Schema.define(version: 20180401220637) 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" t.datetime "date_taken" end @@ -383,92 +362,87 @@ ActiveRecord::Schema.define(version: 20180401220637) 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.integer "parent_seed_id" + 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.integer "parent_seed_id" + 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 "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.boolean "finished", default: false - t.date "finished_at" - t.integer "parent_planting_id" + 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.boolean "finished", default: false + t.date "finished_at" + t.integer "parent_planting_id" + 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" add_foreign_key "plantings", "seeds", column: "parent_seed_id", name: "parent_seed", on_delete: :nullify diff --git a/db/seeds.rb b/db/seeds.rb index 703113be3..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: Growstuff::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/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/lib/haml/filters/growstuff_markdown.rb b/lib/haml/filters/growstuff_markdown.rb index b7b257b53..4e7617c15 100644 --- a/lib/haml/filters/growstuff_markdown.rb +++ b/lib/haml/filters/growstuff_markdown.rb @@ -13,11 +13,11 @@ module Haml::Filters # rubocop:disable Style/ClassAndModuleChildren private - CROP_REGEX = /(? 'foo', - 'uid' => 'bar', - 'info' => { 'nickname' => 'blah' }, + 'provider' => 'foo', + 'uid' => 'bar', + 'info' => { 'nickname' => 'blah' }, 'credentials' => { 'token' => 'blah', 'secret' => 'blah' } } end 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 c42c043f0..c0d331876 100644 --- a/spec/controllers/charts/gardens_controller_spec.rb +++ b/spec/controllers/charts/gardens_controller_spec.rb @@ -8,8 +8,7 @@ describe Charts::GardensController do 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 @@ -20,8 +19,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/comments_controller_spec.rb b/spec/controllers/comments_controller_spec.rb index beee82b33..4d9e9fc6b 100644 --- a/spec/controllers/comments_controller_spec.rb +++ b/spec/controllers/comments_controller_spec.rb @@ -33,16 +33,16 @@ 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) + 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 @@ -54,8 +54,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 } @@ -74,7 +73,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 } @@ -104,7 +103,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/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/gardens_controller_spec.rb b/spec/controllers/gardens_controller_spec.rb index a5620eb01..720ba5a7b 100644 --- a/spec/controllers/gardens_controller_spec.rb +++ b/spec/controllers/gardens_controller_spec.rb @@ -8,14 +8,12 @@ RSpec.describe GardensController, type: :controller do context "when not signed in" do 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 - before { put :create, garden: valid_params } - + before { put :create, params: { garden: valid_params } } it { expect(response).to redirect_to(new_member_session_path) } end @@ -30,20 +28,17 @@ RSpec.describe GardensController, type: :controller do 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 @@ -67,20 +62,17 @@ 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 08f5bb1e4..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 @@ -21,21 +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 @@ -51,14 +48,13 @@ describe HarvestsController 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) } @@ -73,8 +69,7 @@ describe HarvestsController 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 @@ -83,26 +78,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 @@ -111,13 +104,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 @@ -129,7 +122,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) } @@ -140,24 +133,23 @@ 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, 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 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 @@ -167,13 +159,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 @@ -184,8 +176,8 @@ describe HarvestsController do 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) } @@ -197,13 +189,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 bee2ab192..c27d05de9 100644 --- a/spec/controllers/likes_controller_spec.rb +++ b/spec/controllers/likes_controller_spec.rb @@ -9,10 +9,8 @@ describe LikesController do before { sign_in member } describe "POST create" do - before { post :create, post_id: blogpost.id, format: :json } - it { expect(response.content_type).to eq "application/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" } @@ -27,8 +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 14ba85e08..8e6b91283 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,40 @@ 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 - -> { get :show, 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, id: @member2.id }.should raise_error(ActiveRecord::RecordNotFound) + get :show, params: { id: @member2.id } + expect(response).to have_http_status(:not_found) 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..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 @@ -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,8 +75,8 @@ describe NotificationsController do describe "GET new" do it "assigns a recipient" do @recipient = FactoryBot.create(:member) - get :new, recipient_id: @recipient.id - assigns(:recipient).should be_an_instance_of(Member) + get :new, params: { recipient_id: @recipient.id } + expect(assigns(:recipient)).to 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/photo_associations_controller_spec.rb b/spec/controllers/photo_associations_controller_spec.rb index f021d5aa8..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 @@ -19,24 +19,28 @@ 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 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 expect do begin - delete :destroy, valid_params + delete :destroy, params: valid_params rescue StandardError nil end end.not_to change(photo.harvests, :count) end - it { expect { delete :destroy, valid_params }.to raise_error(ActiveRecord::RecordNotFound) } + + it do + delete :destroy, params: valid_params + expect(response).to have_http_status(:not_found) + end end end end diff --git a/spec/controllers/photos_controller_spec.rb b/spec/controllers/photos_controller_spec.rb index e54933c3e..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 @@ -38,8 +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 } @@ -47,28 +46,26 @@ 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 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 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) } @@ -81,15 +78,22 @@ describe PhotosController 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 } @@ -97,14 +101,18 @@ 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 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 @@ -112,21 +120,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 - end.to raise_error('Photos not supported') + 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 @@ -136,15 +142,17 @@ 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 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 end @@ -155,8 +163,10 @@ 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 - end.to raise_error(ActiveRecord::RecordNotFound) + post :create, params: { + photo: { flickr_photo_id: photo.flickr_photo_id }, type: "planting", id: another_planting.id + } + end.to raise_error(ActiveRecord::RecordInvalid) Photo.last.plantings.first.should_not eq another_planting end @@ -164,8 +174,10 @@ 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 - end.to raise_error(ActiveRecord::RecordNotFound) + post :create, params: { + photo: { flickr_photo_id: photo.flickr_photo_id }, type: "harvest", id: another_harvest.id + } + end.to raise_error(ActiveRecord::RecordInvalid) Photo.last.harvests.first.should_not eq another_harvest end 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 1d0fbe64f..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 @@ -19,21 +19,19 @@ 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 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 @@ -42,78 +40,63 @@ 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 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 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 describe 'POST :create' do 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..2dde4f8ae 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.content_type).to 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.content_type).to 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 073c3ba99..0a5fe45b4 100644 --- a/spec/controllers/seeds_controller_spec.rb +++ b/spec/controllers/seeds_controller_spec.rb @@ -4,10 +4,10 @@ describe SeedsController do let(:owner) { FactoryBot.create(:member) } describe "GET index" do - before { get :index, owner: owner.slug } - - it "picks up owner from params" do - 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 @@ -22,9 +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 diff --git a/spec/factories/member.rb b/spec/factories/member.rb index 295d1a408..21eaf99f5 100644 --- a/spec/factories/member.rb +++ b/spec/factories/member.rb @@ -1,13 +1,10 @@ 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 { generate(:login_name) } + login_name { (0...8).map { rand(65..90).chr }.join } password { 'password1' } - email { generate(:email) } + 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/notifications.rb b/spec/factories/notifications.rb index 186c7b905..b0b3dce97 100644 --- a/spec/factories/notifications.rb +++ b/spec/factories/notifications.rb @@ -2,10 +2,9 @@ FactoryBot.define do factory :notification, aliases: [:message] do - sender - recipient + sender { FactoryBot.create :member } + recipient { FactoryBot.create :member } subject { "MyString" } - body { "MyText" } read { false } post 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 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 diff --git a/spec/features/crops/crop_detail_page_spec.rb b/spec/features/crops/crop_detail_page_spec.rb index 62f80d37f..cbc73cf9c 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 @@ -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 03e2bc853..36138fed6 100644 --- a/spec/features/crops/crop_photos_spec.rb +++ b/spec/features/crops/crop_photos_spec.rb @@ -8,18 +8,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: 'photo1.jpg', thumbnail_url: '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: 'photo2.jpg', thumbnail_url: '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: 'photo3.jpg', thumbnail_url: '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: 'photo4.jpg', thumbnail_url: 'thumb4.jpg') + create(:photo, owner: member, + title: 'photo 4', + fullsize_url: "#{valid_server}photo4.jpg", + thumbnail_url: "#{valid_server}thumb4.jpg") end before do 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/home/home_spec.rb b/spec/features/home/home_spec.rb index caf3a6b90..3847d532b 100644 --- a/spec/features/home/home_spec.rb +++ b/spec/features/home/home_spec.rb @@ -4,8 +4,8 @@ feature "home page" do subject { page } 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/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 diff --git a/spec/features/notifications_spec.rb b/spec/features/notifications_spec.rb index 669890fd1..05e6e2b9a 100644 --- a/spec/features/notifications_spec.rb +++ b/spec/features/notifications_spec.rb @@ -2,15 +2,15 @@ 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 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/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 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/features/plantings/planting_a_crop_spec.rb b/spec/features/plantings/planting_a_crop_spec.rb index 0e26d7078..1d8b7f745 100644 --- a/spec/features/plantings/planting_a_crop_spec.rb +++ b/spec/features/plantings/planting_a_crop_spec.rb @@ -222,16 +222,18 @@ 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 - fill_autocomplete "crop", with: "mai" - select_from_autocomplete "maize" - within "form#new_planting" do - check "Mark as finished" - click_button "Save" + describe "Marking a planting as finished without a date" do + 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: Yes (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..67a8b7717 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: 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? heirloom" } + 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/seeds/misc_seeds_spec.rb b/spec/features/seeds/misc_seeds_spec.rb index ec1946d2c..e095ed390 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: 5–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: 5–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: 7" } + 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: 5" } + 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: unknown" } + end end end end diff --git a/spec/features/shared_examples/append_date.rb b/spec/features/shared_examples/append_date.rb index 03e29c5f4..fcb6d515b 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: Yes (no date specified)") end end diff --git a/spec/features/signin_spec.rb b/spec/features/signin_spec.rb index acd9f5295..7f362abf3 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, recipient: recipient } def login fill_in 'Login', with: member.login_name @@ -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 @@ -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_id: recipient.id) expect(current_path).to eq new_member_session_path login expect(current_path).to eq new_notification_path 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/photos_helper_spec.rb b/spec/helpers/photos_helper_spec.rb index 63a45a548..0533b6058 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/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/lib/haml/filters/growstuff_markdown_spec.rb b/spec/lib/haml/filters/growstuff_markdown_spec.rb index 03ad3b0e1..597137f6d 100644 --- a/spec/lib/haml/filters/growstuff_markdown_spec.rb +++ b/spec/lib/haml/filters/growstuff_markdown_spec.rb @@ -7,8 +7,9 @@ def input_link(name) end def output_link(crop, name = nil) - url = Rails.application.routes.url_helpers.crop_url(crop, host: Growstuff::Application.config.host) + 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 @@ -56,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/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 f358a9fca..42b8aa9aa 100644 --- a/spec/models/alternate_name_spec.rb +++ b/spec/models/alternate_name_spec.rb @@ -10,12 +10,22 @@ 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 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 diff --git a/spec/models/crop_spec.rb b/spec/models/crop_spec.rb index ec4ae63c5..1b8de81ed 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 @@ -146,13 +146,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 @@ -163,31 +163,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 @@ -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 @@ -287,14 +287,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 } @@ -305,9 +306,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 } @@ -318,11 +319,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 } @@ -351,7 +352,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 @@ -360,10 +361,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 @@ -563,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/garden_spec.rb b/spec/models/garden_spec.rb index 839ef3445..346e719b5 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, name: 'Springfield Community Garden') } 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 @@ -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..9711d2a59 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 @@ -150,75 +150,75 @@ 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) - @h.to_s.should eq "apricots" + 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) - @h.to_s.should eq "1 individual apricot" + 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) - @h.to_s.should eq "10 individual apricots" + 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) - @h.to_s.should eq "1 bushel of apricots" + 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) - @h.to_s.should eq "1.5 bushels of apricots" + 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) - @h.to_s.should eq "10 bushels of apricots" + 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') - @h.to_s.should eq "apricots weighing 1.2 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') - @h.to_s.should eq "10 bushels of apricots weighing 100 kg" + weight_unit: '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/member_spec.rb b/spec/models/member_spec.rb index 5f278c32c..e1b176378 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -2,44 +2,42 @@ 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 "doesn't show email by default" do - member.show_email.should be(false) + expect(member.show_email).to eq 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 @@ -69,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/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/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 7d4a94221..29241db3f 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, name: 'Springfield Community Garden') } let(:planting) { FactoryBot.create(:planting, crop: crop, garden: garden, owner: garden.owner) } let(:finished_planting) do @@ -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 @@ -219,7 +219,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 @@ -331,94 +331,85 @@ 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 @planting2 = FactoryBot.create(:planting, created_at: 1.minute.ago, - garden: @planting1.garden, - owner: @planting1.owner) - @planting2.photos << FactoryBot.create(:photo) + garden: @planting1.garden, + owner: @planting1.owner) + @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 @@ -426,9 +417,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/post_spec.rb b/spec/models/post_spec.rb index 98880ce2b..eeda089d2 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -1,8 +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 @@ -61,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) } @@ -71,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 @@ -146,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/models/seed_spec.rb b/spec/models/seed_spec.rb index f15a57398..d71fbeda7 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 @@ -152,8 +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,8 +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/rails_helper.rb b/spec/rails_helper.rb index e28dd6e24..5b89cd26d 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! @@ -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' @@ -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 diff --git a/spec/requests/api/v1/crop_request_spec.rb b/spec/requests/api/v1/crop_request_spec.rb index f6cc2d4a5..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,49 +22,47 @@ 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 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) } @@ -73,21 +71,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 c0c33c363..8426dfcb2 100644 --- a/spec/requests/api/v1/gardens_request_spec.rb +++ b/spec/requests/api/v1/gardens_request_spec.rb @@ -6,60 +6,63 @@ 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 - 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 48094de1b..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,50 +22,48 @@ 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 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) } @@ -74,21 +72,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 3767f8a43..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 @@ -58,14 +58,12 @@ RSpec.describe 'Members', type: :request do end 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) } @@ -74,21 +72,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 ed16505f6..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,48 +22,46 @@ 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 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) } @@ -71,21 +69,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 81bc06a14..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,58 +23,58 @@ 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 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) @@ -83,18 +83,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 406412dc8..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,60 +20,58 @@ 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 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 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 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/crops/edit.html.haml_spec.rb b/spec/views/crops/edit.html.haml_spec.rb index 19fac21af..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 - rendered.should have_content "Added by #{@crop.creator} less than a minute ago." + expect(rendered).to have_content "Added by #{@crop.creator} less than a minute ago." end 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 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/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/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/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/members/show.rss.haml_spec.rb b/spec/views/members/show.rss.haml_spec.rb index bb74e1526..6ee321be4 100644 --- a/spec/views/members/show.rss.haml_spec.rb +++ b/spec/views/members/show.rss.haml_spec.rb @@ -4,7 +4,7 @@ describe 'members/show.rss.haml', type: "view" do subject { rendered } 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 end 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 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/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 diff --git a/spec/views/posts/new.html.haml_spec.rb b/spec/views/posts/new.html.haml_spec.rb index fa344e55b..6c6a2ae68 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 #{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 diff --git a/spec/views/posts/show.html.haml_spec.rb b/spec/views/posts/show.html.haml_spec.rb index ffdabfa36..8a9733039 100644 --- a/spec/views/posts/show.html.haml_spec.rb +++ b/spec/views/posts/show.html.haml_spec.rb @@ -2,8 +2,7 @@ require 'rails_helper' describe "posts/show" do subject { rendered } - - let(:author) { FactoryBot.create(:member) } + let(:author) { FactoryBot.create(:member, login_name: 'mary') } before(:each) do controller.stub(:current_user) { nil } @@ -14,12 +13,12 @@ describe "posts/show" 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') } 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 diff --git a/spec/views/scientific_names/edit.html.haml_spec.rb b/spec/views/scientific_names/edit.html.haml_spec.rb index ecaa2056f..249cd249b 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 #{member} less than a minute ago." 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 diff --git a/spec/views/seeds/new.html.haml_spec.rb b/spec/views/seeds/new.html.haml_spec.rb index 5c5cf3ae4..3b33cc6c4 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 set 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 #{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 diff --git a/spec/views/seeds/show.html.haml_spec.rb b/spec/views/seeds/show.html.haml_spec.rb index fd2848f97..2ad38bdcf 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: locally" - 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: locally" + 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