From 4f1b6ca03e8b91221fb69af51fe36fb5d0217d91 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Tue, 17 Oct 2023 21:59:08 +0200 Subject: [PATCH 01/14] Allow setting extra hosts in development environments --- config/environments/development.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/config/environments/development.rb b/config/environments/development.rb index c2434c8c..bf96e5b2 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -75,6 +75,7 @@ Rails.application.configure do # 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 + config.hosts += ENV["EXTRA_HOSTS"].split(':') if ENV["EXTRA_HOSTS"].present? end # For better_errors to work inside Docker we need From dda12956539b7ff5d00a5b827283a10cf58834f5 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Tue, 17 Oct 2023 21:59:37 +0200 Subject: [PATCH 02/14] Use Ruby 3.2.2 --- .docker/ruby/Dockerfile | 2 +- .ruby-version | 2 +- Containerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.docker/ruby/Dockerfile b/.docker/ruby/Dockerfile index 6e6e33d2..5cba21a9 100644 --- a/.docker/ruby/Dockerfile +++ b/.docker/ruby/Dockerfile @@ -1,4 +1,4 @@ -FROM ruby:3.1 +FROM ruby:3.2 USER root diff --git a/.ruby-version b/.ruby-version index ef538c28..be94e6f5 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.1.2 +3.2.2 diff --git a/Containerfile b/Containerfile index 249f7323..adc849a8 100644 --- a/Containerfile +++ b/Containerfile @@ -8,7 +8,7 @@ LABEL org.opencontainers.image.vendor="The Retrospring team" LABEL org.opencontainers.image.url="https://github.com/Retrospring/retrospring" ARG RETROSPRING_VERSION=2023.0131.1 -ARG RUBY_VERSION=3.1.2 +ARG RUBY_VERSION=3.2.2 ARG RUBY_INSTALL_VERSION=0.9.0 ARG BUNDLER_VERSION=2.3.18 From 3539c3264b2e7c1ff777342e442466230876b5e6 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek <6197148+raccube@users.noreply.github.com> Date: Wed, 18 Oct 2023 09:10:28 +0200 Subject: [PATCH 03/14] Bump ruby-install version to 0.9.2 Co-authored-by: Georg Gadinger --- Containerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Containerfile b/Containerfile index adc849a8..73044568 100644 --- a/Containerfile +++ b/Containerfile @@ -9,7 +9,7 @@ LABEL org.opencontainers.image.url="https://github.com/Retrospring/retrospring" ARG RETROSPRING_VERSION=2023.0131.1 ARG RUBY_VERSION=3.2.2 -ARG RUBY_INSTALL_VERSION=0.9.0 +ARG RUBY_INSTALL_VERSION=0.9.2 ARG BUNDLER_VERSION=2.3.18 ENV RAILS_ENV=production From 2a6d88abfcf7f99d2d52130e243aa04152726205 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Wed, 18 Oct 2023 19:29:44 +0200 Subject: [PATCH 04/14] Include type in relationship unique constraint --- ...8_include_type_in_relationship_unique_constraint.rb | 10 ++++++++++ db/schema.rb | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20231018172518_include_type_in_relationship_unique_constraint.rb diff --git a/db/migrate/20231018172518_include_type_in_relationship_unique_constraint.rb b/db/migrate/20231018172518_include_type_in_relationship_unique_constraint.rb new file mode 100644 index 00000000..171fe00f --- /dev/null +++ b/db/migrate/20231018172518_include_type_in_relationship_unique_constraint.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class IncludeTypeInRelationshipUniqueConstraint < ActiveRecord::Migration[6.1] + def change + change_table :relationships do |t| + t.remove_index(%i[source_id target_id]) + t.index(%i[source_id target_id type], unique: true) + end + end +end diff --git a/db/schema.rb b/db/schema.rb index c45fb69c..ea268655 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_05_26_181715) do +ActiveRecord::Schema.define(version: 2023_10_18_172518) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -159,7 +159,7 @@ ActiveRecord::Schema.define(version: 2023_05_26_181715) do t.datetime "created_at" t.datetime "updated_at" t.string "type", null: false - t.index ["source_id", "target_id"], name: "index_relationships_on_source_id_and_target_id", unique: true + t.index ["source_id", "target_id", "type"], name: "index_relationships_on_source_id_and_target_id_and_type", unique: true t.index ["source_id"], name: "index_relationships_on_source_id" t.index ["target_id"], name: "index_relationships_on_target_id" t.index ["type"], name: "index_relationships_on_type" From dc41f150971f11e5ce45f5775541806b84ca342f Mon Sep 17 00:00:00 2001 From: Georg Gadinger Date: Wed, 18 Oct 2023 21:42:19 +0200 Subject: [PATCH 05/14] fix deletion of inbox entries when deleting an user --- app/models/inbox.rb | 3 ++- spec/models/inbox_spec.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 spec/models/inbox_spec.rb diff --git a/app/models/inbox.rb b/app/models/inbox.rb index 1a1a0066..b12beb29 100644 --- a/app/models/inbox.rb +++ b/app/models/inbox.rb @@ -22,7 +22,8 @@ class Inbox < ApplicationRecord end after_destroy do - user.touch(:inbox_updated_at) + # user might not exist at this point (account deleted, records are cleaned up async) + user&.touch(:inbox_updated_at) end def answer(answer_content, user) diff --git a/spec/models/inbox_spec.rb b/spec/models/inbox_spec.rb new file mode 100644 index 00000000..9a00635a --- /dev/null +++ b/spec/models/inbox_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe Inbox, type: :model do + describe "associations" do + it { should belong_to(:user) } + it { should belong_to(:question) } + end + + describe "before_destroy" do + let(:user) { FactoryBot.create(:user) } + let(:question) { FactoryBot.create(:question, author_is_anonymous: true) } + + it "does not fail if the user wants to delete their account" do + Inbox.create(user:, question:) + + # this deletes the User record and enqueues the deletion of all + # associated records in sidekiq + user.destroy! + + # so let's drain the queues + expect { Sidekiq::Worker.drain_all }.not_to raise_error + end + end +end From 8a26232fe6ce1290aacf99eae11686090d60450f Mon Sep 17 00:00:00 2001 From: Georg Gadinger Date: Wed, 18 Oct 2023 21:53:30 +0200 Subject: [PATCH 06/14] bad dog --- app/models/inbox.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/inbox.rb b/app/models/inbox.rb index b12beb29..ada94803 100644 --- a/app/models/inbox.rb +++ b/app/models/inbox.rb @@ -50,7 +50,7 @@ class Inbox < ApplicationRecord user.profile.anon_display_name || APP_CONFIG["anonymous_name"] else question.user.profile.safe_name - end + end, ), icon: notification_icon, body: question.content.truncate(Question::SHORT_QUESTION_MAX_LENGTH), From b53462e4e5847c351f5e3d7ae22c9d09fc50d4a8 Mon Sep 17 00:00:00 2001 From: Georg Gadinger Date: Wed, 18 Oct 2023 21:55:22 +0200 Subject: [PATCH 07/14] update Bundler --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8f56f560..d9e04dd7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -582,4 +582,4 @@ DEPENDENCIES twitter-text BUNDLED WITH - 2.3.18 + 2.4.21 From f5a77bc882a25790e95287988cd17a7e3b3bf6ba Mon Sep 17 00:00:00 2001 From: Georg Gadinger Date: Wed, 18 Oct 2023 21:55:38 +0200 Subject: [PATCH 08/14] update tldv-data --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index d9e04dd7..c9dfd28f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -470,7 +470,7 @@ GEM timeout (0.4.0) tldv (0.1.0) tldv-data (~> 1.0) - tldv-data (1.0.2023031000) + tldv-data (1.0.2023080900) turbo-rails (1.5.0) actionpack (>= 6.0.0) activejob (>= 6.0.0) From a85ce45b25ac921a440d7a94f068a16c73fd070e Mon Sep 17 00:00:00 2001 From: Georg Gadinger Date: Wed, 18 Oct 2023 21:57:09 +0200 Subject: [PATCH 09/14] appease the paw patrol --- app/models/inbox.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/inbox.rb b/app/models/inbox.rb index ada94803..96f3dc84 100644 --- a/app/models/inbox.rb +++ b/app/models/inbox.rb @@ -14,16 +14,16 @@ class Inbox < ApplicationRecord end after_create do - user.touch(:inbox_updated_at) + user.touch(:inbox_updated_at) # rubocop:disable Rails/SkipsModelValidations end after_update do - user.touch(:inbox_updated_at) + user.touch(:inbox_updated_at) # rubocop:disable Rails/SkipsModelValidations end after_destroy do # user might not exist at this point (account deleted, records are cleaned up async) - user&.touch(:inbox_updated_at) + user&.touch(:inbox_updated_at) # rubocop:disable Rails/SkipsModelValidations end def answer(answer_content, user) From c4da510fe71fb0d0d88b88ca730ebeee9d98276c Mon Sep 17 00:00:00 2001 From: Georg Gadinger Date: Wed, 18 Oct 2023 22:13:25 +0200 Subject: [PATCH 10/14] fix deletion of notification entries when deleting a user --- app/models/notification.rb | 7 ++++--- spec/models/notification_spec.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 spec/models/notification_spec.rb diff --git a/app/models/notification.rb b/app/models/notification.rb index e36cad0a..76233c67 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -5,15 +5,16 @@ class Notification < ApplicationRecord belongs_to :target, polymorphic: true after_create do - recipient.touch(:notifications_updated_at) + recipient.touch(:notifications_updated_at) # rubocop:disable Rails/SkipsModelValidations end after_update do - recipient.touch(:notifications_updated_at) + recipient.touch(:notifications_updated_at) # rubocop:disable Rails/SkipsModelValidations end after_destroy do - recipient.touch(:notifications_updated_at) + # recipient might not exist at this point (account deleted, records are cleaned up async) + recipient&.touch(:notifications_updated_at) # rubocop:disable Rails/SkipsModelValidations end class << self diff --git a/spec/models/notification_spec.rb b/spec/models/notification_spec.rb new file mode 100644 index 00000000..9bf3aea8 --- /dev/null +++ b/spec/models/notification_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe Notification, type: :model do + describe "associations" do + it { should belong_to(:recipient) } + it { should belong_to(:target) } + end + + describe "before_destroy" do + let(:user) { FactoryBot.create(:user) } + let(:answer) { FactoryBot.create(:answer, user: FactoryBot.create(:user)) } + + it "does not fail if the user wants to delete their account" do + Notification::QuestionAnswered.create(recipient: user, target: answer) + + # this deletes the User record and enqueues the deletion of all + # associated records in sidekiq + user.destroy! + + # so let's drain the queues + expect { Sidekiq::Worker.drain_all }.not_to raise_error + end + end +end From e37532e0ebba5fd64f84727ae4a6e05b9cc2a1dc Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Thu, 19 Oct 2023 10:50:00 +0200 Subject: [PATCH 11/14] Bump version to 2023.1019.0 --- lib/retrospring/version.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/retrospring/version.rb b/lib/retrospring/version.rb index 9046c9d6..5b2806e4 100644 --- a/lib/retrospring/version.rb +++ b/lib/retrospring/version.rb @@ -17,9 +17,9 @@ module Retrospring def month = 10 - def day = 17 + def day = 19 - def patch = 1 + def patch = 0 def suffix = "" From 9cb6b25cc72c8bf48e67dd3215d641994de981b2 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Wed, 18 Oct 2023 23:25:16 +0200 Subject: [PATCH 12/14] Disable `Metrics/*Complexity` cops --- .rubocop.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index 12052185..0d6c9020 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -46,7 +46,10 @@ Metrics/ClassLength: Enabled: false Metrics/CyclomaticComplexity: - Severity: refactor + Enabled: false + +Metrics/PerceivedComplexity: + Enabled: false Metrics/ModuleLength: Enabled: false From 25adb11df3701a119f56d79c0759bd1ef6cd3131 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Wed, 18 Oct 2023 23:26:10 +0200 Subject: [PATCH 13/14] Fix rubocop nits in helper code and specs --- app/helpers/ajax_helper.rb | 2 + .../application_helper/graph_methods.rb | 10 +-- .../application_helper/title_methods.rb | 2 +- app/helpers/bootstrap_helper.rb | 21 +++--- app/helpers/feedback_helper.rb | 2 +- app/helpers/markdown_helper.rb | 2 +- app/helpers/social_helper/telegram_methods.rb | 2 +- app/helpers/social_helper/tumblr_methods.rb | 18 ++--- app/helpers/social_helper/twitter_methods.rb | 20 +++--- app/helpers/theme_helper.rb | 2 +- .../application_helper/graph_methods_spec.rb | 70 +++++++++++-------- .../application_helper/title_methods_spec.rb | 10 +-- spec/helpers/bootstrap_helper_spec.rb | 70 ++++++++++--------- spec/helpers/feedback_helper_spec.rb | 6 +- spec/helpers/markdown_helper_spec.rb | 4 +- .../social_helper/telegram_methods_spec.rb | 4 +- .../social_helper/tumblr_methods_spec.rb | 44 ++++++------ .../social_helper/twitter_methods_spec.rb | 60 ++++++++-------- spec/helpers/social_helper_spec.rb | 3 +- spec/helpers/theme_helper_spec.rb | 36 +++++----- spec/helpers/user_helper_spec.rb | 14 ++-- 21 files changed, 214 insertions(+), 188 deletions(-) diff --git a/app/helpers/ajax_helper.rb b/app/helpers/ajax_helper.rb index 3f1a4867..6a7aa743 100644 --- a/app/helpers/ajax_helper.rb +++ b/app/helpers/ajax_helper.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + module AjaxHelper end diff --git a/app/helpers/application_helper/graph_methods.rb b/app/helpers/application_helper/graph_methods.rb index cde48656..a9487db9 100644 --- a/app/helpers/application_helper/graph_methods.rb +++ b/app/helpers/application_helper/graph_methods.rb @@ -4,13 +4,13 @@ module ApplicationHelper::GraphMethods # Creates tags for OpenGraph properties from a hash # @param values [Hash] def opengraph_meta_tags(values) - safe_join(values.map { |name, content| tag.meta(property: name, content: content) }, "\n") + safe_join(values.map { |name, content| tag.meta(property: name, content:) }, "\n") end # Creates tags from a hash # @param values [Hash] def meta_tags(values) - safe_join(values.map { |name, content| tag.meta(name: name, content: content) }, "\n") + safe_join(values.map { |name, content| tag.meta(name:, content:) }, "\n") end # @param user [User] @@ -22,7 +22,7 @@ module ApplicationHelper::GraphMethods "og:url": user_url(user), "og:description": user.profile.description, "og:site_name": APP_CONFIG["site_name"], - "profile:username": user.screen_name + "profile:username": user.screen_name, }) end @@ -33,7 +33,7 @@ module ApplicationHelper::GraphMethods "twitter:site": "@retrospring", "twitter:title": user.profile.motivation_header.presence || "Ask me anything!", "twitter:description": "Ask #{user.profile.safe_name} anything on Retrospring", - "twitter:image": full_profile_picture_url(user) + "twitter:image": full_profile_picture_url(user), }) end @@ -45,7 +45,7 @@ module ApplicationHelper::GraphMethods "og:image": full_profile_picture_url(answer.user), "og:url": answer_url(answer.user.screen_name, answer.id), "og:description": answer.content, - "og:site_name": APP_CONFIG["site_name"] + "og:site_name": APP_CONFIG["site_name"], }) end diff --git a/app/helpers/application_helper/title_methods.rb b/app/helpers/application_helper/title_methods.rb index b80deee3..9784e646 100644 --- a/app/helpers/application_helper/title_methods.rb +++ b/app/helpers/application_helper/title_methods.rb @@ -28,7 +28,7 @@ module ApplicationHelper::TitleMethods def question_title(question) context_user = question.answers&.first&.user if question.direct name = user_screen_name question.user, - context_user: context_user, + context_user:, author_identifier: question.author_is_anonymous ? question.author_identifier : nil, url: false generate_title name, "asked", question.content diff --git a/app/helpers/bootstrap_helper.rb b/app/helpers/bootstrap_helper.rb index 55544265..e6ae6981 100644 --- a/app/helpers/bootstrap_helper.rb +++ b/app/helpers/bootstrap_helper.rb @@ -25,24 +25,24 @@ module BootstrapHelper "#{content_tag(:i, '', class: "fa fa-#{options[:icon]}")} #{body}" end end - if options[:badge].present? || options.dig(:badge_attr, :data)&.has_key?(:controller) + if options[:badge].present? || options.dig(:badge_attr, :data)&.key?(:controller) badge_class = [ "badge", ("badge-#{options[:badge_color]}" unless options[:badge_color].nil?), ("badge-pill" if options[:badge_pill]) ].compact.join(" ") - body += " #{content_tag(:span, options[:badge], class: badge_class, **options[:badge_attr])}".html_safe + body += " #{content_tag(:span, options[:badge], class: badge_class, **options[:badge_attr])}".html_safe # rubocop:disable Rails/OutputSafety end - content_tag(:li, link_to(body.html_safe, path, class: "nav-link", data: { hotkey: options[:hotkey] }), class: classes, id: options[:id]) + content_tag(:li, link_to(body.html_safe, path, class: "nav-link", data: { hotkey: options[:hotkey] }), class: classes, id: options[:id]) # rubocop:disable Rails/OutputSafety end def list_group_item(body, path, options = {}) options = { badge: nil, badge_color: nil, - class: "" + class: "", }.merge(options) classes = [ @@ -54,13 +54,18 @@ module BootstrapHelper unless options[:badge].nil? || (options[:badge]).zero? # TODO: make this prettier? - body << " #{ - content_tag(:span, options[:badge], class: "badge#{ + badge = content_tag(:span, options[:badge], class: "badge#{ " badge-#{options[:badge_color]}" unless options[:badge_color].nil? - }")}" + }",) end - content_tag(:a, body.html_safe, href: path, class: classes) + html = if badge + "#{body} #{badge}" + else + body + end + + content_tag(:a, html.html_safe, href: path, class: classes) # rubocop:disable Rails/OutputSafety end def tooltip(body, tooltip_content, placement = "bottom") diff --git a/app/helpers/feedback_helper.rb b/app/helpers/feedback_helper.rb index ff51d91b..ac47c27a 100644 --- a/app/helpers/feedback_helper.rb +++ b/app/helpers/feedback_helper.rb @@ -8,7 +8,7 @@ module FeedbackHelper avatarURL: current_user.profile_picture.url(:large), name: current_user.screen_name, id: current_user.id, - email: current_user.email + email: current_user.email, } JWT.encode(user_data, APP_CONFIG.dig("canny", "sso")) diff --git a/app/helpers/markdown_helper.rb b/app/helpers/markdown_helper.rb index ef634209..6ae71c17 100644 --- a/app/helpers/markdown_helper.rb +++ b/app/helpers/markdown_helper.rb @@ -30,7 +30,7 @@ module MarkdownHelper def raw_markdown(content) renderer = Redcarpet::Render::HTML.new(**MARKDOWN_RENDERER_OPTS) md = Redcarpet::Markdown.new(renderer, **MARKDOWN_OPTS) - raw md.render content + raw md.render content # rubocop:disable Rails/OutputSafety end def get_markdown(path, relative_to = Rails.root) diff --git a/app/helpers/social_helper/telegram_methods.rb b/app/helpers/social_helper/telegram_methods.rb index ec13f9ea..046d2392 100644 --- a/app/helpers/social_helper/telegram_methods.rb +++ b/app/helpers/social_helper/telegram_methods.rb @@ -15,7 +15,7 @@ module SocialHelper::TelegramMethods id: answer.id, username: answer.user.screen_name, host: APP_CONFIG["hostname"], - protocol: (APP_CONFIG["https"] ? :https : :http) + protocol: (APP_CONFIG["https"] ? :https : :http), ) %(https://t.me/share/url?url=#{CGI.escape(url)}&text=#{CGI.escape(telegram_text(answer))}) diff --git a/app/helpers/social_helper/tumblr_methods.rb b/app/helpers/social_helper/tumblr_methods.rb index 868f31d3..683199b9 100644 --- a/app/helpers/social_helper/tumblr_methods.rb +++ b/app/helpers/social_helper/tumblr_methods.rb @@ -1,4 +1,6 @@ -require 'cgi' +# frozen_string_literal: true + +require "cgi" module SocialHelper::TumblrMethods def tumblr_title(answer) @@ -13,10 +15,10 @@ module SocialHelper::TumblrMethods def tumblr_body(answer) answer_url = answer_url( - id: answer.id, - username: answer.user.screen_name, - host: APP_CONFIG['hostname'], - protocol: (APP_CONFIG['https'] ? :https : :http) + id: answer.id, + username: answer.user.screen_name, + host: APP_CONFIG["hostname"], + protocol: (APP_CONFIG["https"] ? :https : :http), ) "#{answer.content}\n\n[Smile or comment on the answer here](#{answer_url})" @@ -24,10 +26,10 @@ module SocialHelper::TumblrMethods def tumblr_share_url(answer) answer_url = answer_url( - id: answer.id, + id: answer.id, username: answer.user.screen_name, - host: APP_CONFIG['hostname'], - protocol: (APP_CONFIG['https'] ? :https : :http) + host: APP_CONFIG["hostname"], + protocol: (APP_CONFIG["https"] ? :https : :http), ) "https://www.tumblr.com/widgets/share/tool?shareSource=legacy&posttype=text&title=#{CGI.escape(tumblr_title(answer))}&url=#{CGI.escape(answer_url)}&caption=&content=#{CGI.escape(tumblr_body(answer))}" diff --git a/app/helpers/social_helper/twitter_methods.rb b/app/helpers/social_helper/twitter_methods.rb index 7f025f71..fd143392 100644 --- a/app/helpers/social_helper/twitter_methods.rb +++ b/app/helpers/social_helper/twitter_methods.rb @@ -1,21 +1,23 @@ -require 'cgi' +# frozen_string_literal: true + +require "cgi" module SocialHelper::TwitterMethods include MarkdownHelper def prepare_tweet(answer, post_tag = nil) - question_content = twitter_markdown answer.question.content.gsub(/\@(\w+)/, '\1') + question_content = twitter_markdown answer.question.content.gsub(/@(\w+)/, '\1') original_question_length = question_content.length answer_content = twitter_markdown answer.content original_answer_length = answer_content.length answer_url = answer_url( - id: answer.id, + id: answer.id, username: answer.user.screen_name, - host: APP_CONFIG['hostname'], - protocol: (APP_CONFIG['https'] ? :https : :http) + host: APP_CONFIG["hostname"], + protocol: (APP_CONFIG["https"] ? :https : :http), ) - parsed_tweet = { :valid => false } + parsed_tweet = { valid: false } tweet_text = "" until parsed_tweet[:valid] @@ -23,14 +25,14 @@ module SocialHelper::TwitterMethods shortened_answer = "#{answer_content[0..123]}#{'…' if original_answer_length > [124, answer_content.length].min}" components = [ shortened_question, - '—', + "—", shortened_answer, post_tag, answer_url ] - tweet_text = components.compact.join(' ') + tweet_text = components.compact.join(" ") - parsed_tweet = Twitter::TwitterText::Validation::parse_tweet(tweet_text) + parsed_tweet = Twitter::TwitterText::Validation.parse_tweet(tweet_text) question_content = question_content[0..-2] answer_content = answer_content[0..-2] diff --git a/app/helpers/theme_helper.rb b/app/helpers/theme_helper.rb index 4c1a735f..9cd04910 100644 --- a/app/helpers/theme_helper.rb +++ b/app/helpers/theme_helper.rb @@ -25,7 +25,7 @@ module ThemeHelper "input_color" => "input-bg", "input_text" => "input-text", "input_placeholder" => "input-placeholder", - "muted_text" => "muted-text" + "muted_text" => "muted-text", }.freeze def render_theme diff --git a/spec/helpers/application_helper/graph_methods_spec.rb b/spec/helpers/application_helper/graph_methods_spec.rb index 63baa848..1d79b78c 100644 --- a/spec/helpers/application_helper/graph_methods_spec.rb +++ b/spec/helpers/application_helper/graph_methods_spec.rb @@ -1,20 +1,22 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" -describe ApplicationHelper::GraphMethods, :type => :helper do +describe ApplicationHelper::GraphMethods, type: :helper do describe "#user_opengraph" do context "sample user" do - let(:user) { FactoryBot.create(:user, - profile: { display_name: 'Cunes', - description: 'A bunch of raccoons in a trenchcoat.' }, - screen_name: 'raccoons') } + let(:user) do + FactoryBot.create(:user, + profile: { display_name: "Cunes", + description: "A bunch of raccoons in a trenchcoat.", }, + screen_name: "raccoons",) + end subject { user_opengraph(user) } - it 'should generate a matching OpenGraph structure for a user' do - allow(APP_CONFIG).to receive(:[]).with('site_name').and_return('pineapplespring') - expect(subject).to eq(<<~EOS.chomp) + it "should generate a matching OpenGraph structure for a user" do + allow(APP_CONFIG).to receive(:[]).with("site_name").and_return("pineapplespring") + expect(subject).to eq(<<~META.chomp) @@ -22,55 +24,63 @@ describe ApplicationHelper::GraphMethods, :type => :helper do - EOS + META end end end describe "#user_twitter_card" do context "sample user" do - let(:user) { FactoryBot.create(:user, - profile: { - display_name: '', - description: 'A bunch of raccoons in a trenchcoat.'}, - screen_name: 'raccoons') } + let(:user) do + FactoryBot.create(:user, + profile: { + display_name: "", + description: "A bunch of raccoons in a trenchcoat.", + }, + screen_name: "raccoons",) + end subject { user_twitter_card(user) } - it 'should generate a matching OpenGraph structure for a user' do - expect(subject).to eq(<<~EOS.chomp) + it "should generate a matching OpenGraph structure for a user" do + expect(subject).to eq(<<~META.chomp) - EOS + META end end end describe "#answer_opengraph" do context "sample user and answer" do - let!(:user) { FactoryBot.create(:user, - profile: { - display_name: '', - description: 'A bunch of raccoons in a trenchcoat.'}, - screen_name: 'raccoons') } - let(:answer) { FactoryBot.create(:answer, - user_id: user.id,) } + let!(:user) do + FactoryBot.create(:user, + profile: { + display_name: "", + description: "A bunch of raccoons in a trenchcoat.", + }, + screen_name: "raccoons",) + end + let(:answer) do + FactoryBot.create(:answer, + user_id: user.id,) + end subject { answer_opengraph(answer) } - it 'should generate a matching OpenGraph structure for a user' do - allow(APP_CONFIG).to receive(:[]).with('site_name').and_return('pineapplespring') - expect(subject).to eq(<<~EOS.chomp) + it "should generate a matching OpenGraph structure for a user" do + allow(APP_CONFIG).to receive(:[]).with("site_name").and_return("pineapplespring") + expect(subject).to eq(<<~META.chomp) - EOS + META end end end -end \ No newline at end of file +end diff --git a/spec/helpers/application_helper/title_methods_spec.rb b/spec/helpers/application_helper/title_methods_spec.rb index 2b3c8bf0..92dc74a8 100644 --- a/spec/helpers/application_helper/title_methods_spec.rb +++ b/spec/helpers/application_helper/title_methods_spec.rb @@ -12,8 +12,8 @@ describe ApplicationHelper::TitleMethods, type: :helper do "anonymous_name" => "Anonymous", "https" => true, "items_per_page" => 5, - "sharing" => {} - }) + "sharing" => {}, + },) user.profile.display_name = "Cool Man" user.profile.save! @@ -42,7 +42,7 @@ describe ApplicationHelper::TitleMethods, type: :helper do context "user has custom anonymous display name" do before do - FactoryBot.create(:answer, question: question, user: user) + FactoryBot.create(:answer, question:, user:) user.profile.anon_display_name = "Amogus" user.profile.save! end @@ -55,9 +55,9 @@ describe ApplicationHelper::TitleMethods, type: :helper do describe "#answer_title" do let(:answer) do - FactoryBot.create(:answer, user: user, + FactoryBot.create(:answer, user:, content: "a", - question_content: "q") + question_content: "q",) end it "should generate a proper title" do diff --git a/spec/helpers/bootstrap_helper_spec.rb b/spec/helpers/bootstrap_helper_spec.rb index 0f31f85e..2f335560 100644 --- a/spec/helpers/bootstrap_helper_spec.rb +++ b/spec/helpers/bootstrap_helper_spec.rb @@ -1,45 +1,47 @@ +# frozen_string_literal: true + require "rails_helper" -describe BootstrapHelper, :type => :helper do +describe BootstrapHelper, type: :helper do include ActiveSupport::Testing::TimeHelpers - describe '#nav_entry' do - it 'should return a HTML navigation item which links to a given address' do + describe "#nav_entry" do + it "should return a HTML navigation item which links to a given address" do allow(self).to receive(:current_page?).and_return(false) - expect(nav_entry('Example', '/example')).to( - eq('') + expect(nav_entry("Example", "/example")).to( + eq(''), ) end - it 'should return with an active attribute if the link matches the current URL' do + it "should return with an active attribute if the link matches the current URL" do allow(self).to receive(:current_page?).and_return(true) - expect(nav_entry('Example', '/example')).to( - eq('') + expect(nav_entry("Example", "/example")).to( + eq(''), ) end - it 'should include an icon if given' do + it "should include an icon if given" do allow(self).to receive(:current_page?).and_return(false) - expect(nav_entry('Example', '/example', icon: 'beaker')).to( - eq('') + expect(nav_entry("Example", "/example", icon: "beaker")).to( + eq(''), ) end - it 'should only include an icon if wanted' do + it "should only include an icon if wanted" do allow(self).to receive(:current_page?).and_return(false) - expect(nav_entry('Example', '/example', icon: 'beaker', icon_only: true)).to( - eq('') + expect(nav_entry("Example", "/example", icon: "beaker", icon_only: true)).to( + eq(''), ) end - it 'should include a badge if given' do + it "should include a badge if given" do allow(self).to receive(:current_page?).and_return(false) - expect(nav_entry('Example', '/example', badge: 3)).to( - eq('') + expect(nav_entry("Example", "/example", badge: 3)).to( + eq(''), ) - expect(nav_entry('Example', '/example', badge: 3, badge_color: 'primary', badge_pill: true)).to( - eq('') + expect(nav_entry("Example", "/example", badge: 3, badge_color: "primary", badge_pill: true)).to( + eq(''), ) end @@ -52,51 +54,51 @@ describe BootstrapHelper, :type => :helper do end describe "#list_group_item" do - it 'should return a HTML navigation item which links to a given address' do + it "should return a HTML navigation item which links to a given address" do allow(self).to receive(:current_page?).and_return(false) - expect(list_group_item('Example', '/example')).to( - eq('Example') + expect(list_group_item("Example", "/example")).to( + eq('Example'), ) end - it 'should return with an active attribute if the link matches the current URL' do + it "should return with an active attribute if the link matches the current URL" do allow(self).to receive(:current_page?).and_return(true) - expect(list_group_item('Example', '/example')).to( - eq('Example') + expect(list_group_item("Example", "/example")).to( + eq('Example'), ) end - it 'should include a badge if given' do + it "should include a badge if given" do allow(self).to receive(:current_page?).and_return(false) - expect(list_group_item('Example', '/example', badge: 3)).to( - eq('Example 3') + expect(list_group_item("Example", "/example", badge: 3)).to( + eq('Example 3'), ) end end describe "#bootstrap_color" do - it 'should map error and alert to danger' do + it "should map error and alert to danger" do expect(bootstrap_color("error")).to eq("danger") expect(bootstrap_color("alert")).to eq("danger") end - it 'should map notice to info' do + it "should map notice to info" do expect(bootstrap_color("notice")).to eq("info") end - it 'should return any uncovered value' do + it "should return any uncovered value" do expect(bootstrap_color("success")).to eq("success") end end describe "#tooltip" do - it 'should return the proper markup' do + it "should return the proper markup" do expect(tooltip("Example Text", "This is in a tooltip")).to eq("Example Text") end end describe "#time_tooltip" do - it 'should return a tooltip with proper time values' do + it "should return a tooltip with proper time values" do travel_to(Time.utc(1984)) do @user = FactoryBot.create(:user) travel 10.minutes @@ -107,7 +109,7 @@ describe BootstrapHelper, :type => :helper do end describe "#hidespan" do - it 'should return the proper markup' do + it "should return the proper markup" do expect(hidespan("Hidden Text", "d-none")).to eq("Hidden Text") end end diff --git a/spec/helpers/feedback_helper_spec.rb b/spec/helpers/feedback_helper_spec.rb index 13d2245e..c5aa16df 100644 --- a/spec/helpers/feedback_helper_spec.rb +++ b/spec/helpers/feedback_helper_spec.rb @@ -11,9 +11,9 @@ describe FeedbackHelper, type: :helper do "canny" => { sso: "sso", feature_board: "feature", - bug_board: "bug" - } - }) + bug_board: "bug", + }, + },) end describe "#canny_token" do diff --git a/spec/helpers/markdown_helper_spec.rb b/spec/helpers/markdown_helper_spec.rb index b4c22b76..39270551 100644 --- a/spec/helpers/markdown_helper_spec.rb +++ b/spec/helpers/markdown_helper_spec.rb @@ -10,8 +10,8 @@ describe MarkdownHelper, type: :helper do "items_per_page" => 5, "allowed_hosts" => [ "twitter.com" - ] - }) + ], + },) end describe "#markdown" do diff --git a/spec/helpers/social_helper/telegram_methods_spec.rb b/spec/helpers/social_helper/telegram_methods_spec.rb index 5e180373..3a72cff8 100644 --- a/spec/helpers/social_helper/telegram_methods_spec.rb +++ b/spec/helpers/social_helper/telegram_methods_spec.rb @@ -9,7 +9,7 @@ describe SocialHelper::TelegramMethods, type: :helper do :answer, user:, content: "this is an answer\nwith multiple lines\nand **FORMATTING**", - question_content: "this is a question .... or is it?" + question_content: "this is a question .... or is it?", ) end @@ -18,7 +18,7 @@ describe SocialHelper::TelegramMethods, type: :helper do "hostname" => "example.com", "https" => true, "items_per_page" => 5, - }) + },) end describe "#telegram_text" do diff --git a/spec/helpers/social_helper/tumblr_methods_spec.rb b/spec/helpers/social_helper/tumblr_methods_spec.rb index 512a35e7..29ba43d0 100644 --- a/spec/helpers/social_helper/tumblr_methods_spec.rb +++ b/spec/helpers/social_helper/tumblr_methods_spec.rb @@ -1,33 +1,35 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" -describe SocialHelper::TumblrMethods, :type => :helper do +describe SocialHelper::TumblrMethods, type: :helper do let(:user) { FactoryBot.create(:user) } - let(:answer) { FactoryBot.create(:answer, user: user, - content: 'aaaa', - question_content: 'q') } + let(:answer) do + FactoryBot.create(:answer, user:, + content: "aaaa", + question_content: "q",) + end before do stub_const("APP_CONFIG", { - 'hostname' => 'example.com', - 'anonymous_name' => 'Anonymous', - 'https' => true, - 'items_per_page' => 5, - 'sharing' => {} - }) + "hostname" => "example.com", + "anonymous_name" => "Anonymous", + "https" => true, + "items_per_page" => 5, + "sharing" => {}, + },) end - describe '#tumblr_title' do - context 'Asker is anonymous' do + describe "#tumblr_title" do + context "Asker is anonymous" do subject { tumblr_title(answer) } - it 'should return a proper title' do - expect(subject).to eq('Anonymous asked: q') + it "should return a proper title" do + expect(subject).to eq("Anonymous asked: q") end end - context 'Asker is known' do + context "Asker is known" do before do @user = FactoryBot.create(:user) answer.question.user = @user @@ -36,24 +38,24 @@ describe SocialHelper::TumblrMethods, :type => :helper do subject { tumblr_title(answer) } - it 'should return a proper title' do + it "should return a proper title" do expect(subject).to eq("#{answer.question.user.profile.display_name} asked: q") end end end - describe '#tumblr_body' do + describe "#tumblr_body" do subject { tumblr_body(answer) } - it 'should return a proper body' do + it "should return a proper body" do expect(subject).to eq("aaaa\n\n[Smile or comment on the answer here](https://example.com/@#{answer.user.screen_name}/a/#{answer.id})") end end - describe '#tumblr_share_url' do + describe "#tumblr_share_url" do subject { tumblr_share_url(answer) } - it 'should return a proper share link' do + it "should return a proper share link" do expect(subject).to eq("https://www.tumblr.com/widgets/share/tool?shareSource=legacy&posttype=text&title=#{CGI.escape(tumblr_title(answer))}&url=#{CGI.escape("https://example.com/@#{answer.user.screen_name}/a/#{answer.id}")}&caption=&content=#{CGI.escape(tumblr_body(answer))}") end end diff --git a/spec/helpers/social_helper/twitter_methods_spec.rb b/spec/helpers/social_helper/twitter_methods_spec.rb index 7074f6e6..cd97fdcc 100644 --- a/spec/helpers/social_helper/twitter_methods_spec.rb +++ b/spec/helpers/social_helper/twitter_methods_spec.rb @@ -1,72 +1,74 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" -describe SocialHelper::TwitterMethods, :type => :helper do +describe SocialHelper::TwitterMethods, type: :helper do let(:user) { FactoryBot.create(:user) } - let(:question_content) { 'q' * 255 } - let(:answer_content) { 'a' * 255 } - let(:answer) { FactoryBot.create(:answer, user: user, - content: answer_content, - question_content: question_content) } + let(:question_content) { "q" * 255 } + let(:answer_content) { "a" * 255 } + let(:answer) do + FactoryBot.create(:answer, user:, + content: answer_content, + question_content:,) + end before do stub_const("APP_CONFIG", { - 'hostname' => 'example.com', - 'https' => true, - 'items_per_page' => 5 - }) + "hostname" => "example.com", + "https" => true, + "items_per_page" => 5, + },) end - describe '#prepare_tweet' do - context 'when the question and answer need to be shortened' do + describe "#prepare_tweet" do + context "when the question and answer need to be shortened" do subject { prepare_tweet(answer) } - it 'should return a properly formatted tweet' do + it "should return a properly formatted tweet" do expect(subject).to eq("#{'q' * 123}… — #{'a' * 124}… https://example.com/@#{user.screen_name}/a/#{answer.id}") end end - context 'when a suffix has been passed' do - let(:question_content) { 'question' } - let(:answer_content) { 'answer' } + context "when a suffix has been passed" do + let(:question_content) { "question" } + let(:answer_content) { "answer" } - subject { prepare_tweet(answer, '#askracc') } + subject { prepare_tweet(answer, "#askracc") } - it 'should include the suffix after the link' do + it "should include the suffix after the link" do expect(subject).to eq("question — answer #askracc https://example.com/@#{user.screen_name}/a/#{answer.id}") end end - context 'when a suffix has been passed and the tweet needs to be shortened' do - subject { prepare_tweet(answer, '#askracc') } + context "when a suffix has been passed and the tweet needs to be shortened" do + subject { prepare_tweet(answer, "#askracc") } - it 'should shorten the tweet while keeping the suffix intact' do + it "should shorten the tweet while keeping the suffix intact" do expect(subject).to eq("#{'q' * 120}… — #{'a' * 120}… #askracc https://example.com/@#{user.screen_name}/a/#{answer.id}") end end - context 'when the question and answer are short' do + context "when the question and answer are short" do before do - answer.question.content = 'Why are raccoons so good?' + answer.question.content = "Why are raccoons so good?" answer.question.save! - answer.content = 'Because they are good cunes.' + answer.content = "Because they are good cunes." answer.save! end subject { prepare_tweet(answer) } - it 'should return a properly formatted tweet' do + it "should return a properly formatted tweet" do expect(subject).to eq("#{answer.question.content} — #{answer.content} https://example.com/@#{user.screen_name}/a/#{answer.id}") end end end - describe '#twitter_share_url' do + describe "#twitter_share_url" do subject { twitter_share_url(answer) } - it 'should return a proper share link' do + it "should return a proper share link" do expect(subject).to eq("https://twitter.com/intent/tweet?text=#{CGI.escape(prepare_tweet(answer))}") end end -end \ No newline at end of file +end diff --git a/spec/helpers/social_helper_spec.rb b/spec/helpers/social_helper_spec.rb index 1c165eb0..b56d6ab3 100644 --- a/spec/helpers/social_helper_spec.rb +++ b/spec/helpers/social_helper_spec.rb @@ -18,8 +18,7 @@ describe SocialHelper, type: :helper do "hostname" => "example.com", "https" => true, "items_per_page" => 5, - }, - ) + },) end describe "#answer_share_url" do diff --git a/spec/helpers/theme_helper_spec.rb b/spec/helpers/theme_helper_spec.rb index 3a72ed34..f7de9441 100644 --- a/spec/helpers/theme_helper_spec.rb +++ b/spec/helpers/theme_helper_spec.rb @@ -2,7 +2,7 @@ require "rails_helper" -describe ThemeHelper, :type => :helper do +describe ThemeHelper, type: :helper do describe "#render_theme" do context "when target page doesn't have a theme" do it "returns no theme" do @@ -18,7 +18,7 @@ describe ThemeHelper, :type => :helper do end it "returns a theme" do - expect(helper.render_theme).to include('