From d3fa62d4c2cc7e82b46bca8c349c42a20745fc87 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 20 Apr 2025 16:21:08 +0200 Subject: [PATCH 01/13] Inline touch check thats only used once Not worth loading an entire file for this. --- app/javascript/controllers/soft_keyboard_controller.js | 4 ++-- app/javascript/helpers/navigator_helpers.js | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) delete mode 100644 app/javascript/helpers/navigator_helpers.js diff --git a/app/javascript/controllers/soft_keyboard_controller.js b/app/javascript/controllers/soft_keyboard_controller.js index becc36c52..75fb4f2ee 100644 --- a/app/javascript/controllers/soft_keyboard_controller.js +++ b/app/javascript/controllers/soft_keyboard_controller.js @@ -1,10 +1,10 @@ import { Controller } from "@hotwired/stimulus" import { nextEventNamed } from "helpers/timing_helpers" -import { isTouchDevice } from "helpers/navigator_helpers" export default class extends Controller { + // Only load for touch devices static get shouldLoad() { - return isTouchDevice() + return "ontouchstart" in window && navigator.maxTouchPoints > 0 } // Use a fake input to trigger the soft keyboard on actions that load async content diff --git a/app/javascript/helpers/navigator_helpers.js b/app/javascript/helpers/navigator_helpers.js deleted file mode 100644 index b75f62875..000000000 --- a/app/javascript/helpers/navigator_helpers.js +++ /dev/null @@ -1,3 +0,0 @@ -export function isTouchDevice() { - return "ontouchstart" in window && navigator.maxTouchPoints > 0 -} From 48a6c052505ae4ab8d54357be0aa87e6d98f36e1 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 20 Apr 2025 16:23:15 +0200 Subject: [PATCH 02/13] No longer used --- app/javascript/initializers/custom_stream_actions.js | 8 -------- app/javascript/initializers/index.js | 1 - lib/rails_ext/turbo_streams_action_helper.rb | 7 ------- 3 files changed, 16 deletions(-) delete mode 100644 app/javascript/initializers/custom_stream_actions.js delete mode 100644 lib/rails_ext/turbo_streams_action_helper.rb diff --git a/app/javascript/initializers/custom_stream_actions.js b/app/javascript/initializers/custom_stream_actions.js deleted file mode 100644 index fecb25ff5..000000000 --- a/app/javascript/initializers/custom_stream_actions.js +++ /dev/null @@ -1,8 +0,0 @@ -import { Turbo } from "@hotwired/turbo-rails" - -Turbo.StreamActions.set_css_variable = function() { - const name = this.getAttribute("name") - const value = this.getAttribute("value") - - this.targetElements.forEach(element => element.style.setProperty(name, value)) -} diff --git a/app/javascript/initializers/index.js b/app/javascript/initializers/index.js index 01fdd9220..10fb36975 100644 --- a/app/javascript/initializers/index.js +++ b/app/javascript/initializers/index.js @@ -1,2 +1 @@ import "initializers/current" -import "initializers/custom_stream_actions" diff --git a/lib/rails_ext/turbo_streams_action_helper.rb b/lib/rails_ext/turbo_streams_action_helper.rb deleted file mode 100644 index 001c9706e..000000000 --- a/lib/rails_ext/turbo_streams_action_helper.rb +++ /dev/null @@ -1,7 +0,0 @@ -module TurboStreamsActionsHelper - def set_css_variable(target, name:, value:) - tag.turbo_stream target: target, action: "set_css_variable", name:, value: - end -end - -Turbo::Streams::TagBuilder.prepend(TurboStreamsActionsHelper) From dc2205ee79d621b0820cf67c23cb229727c178a5 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 21 Apr 2025 09:18:42 +0200 Subject: [PATCH 03/13] Cards have a not-null schema and collections destroy all cards on deletion This shouldnt be necessary. Maybe remnant of some previous design? --- app/models/card.rb | 2 +- test/models/card_test.rb | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/app/models/card.rb b/app/models/card.rb index 9c8ce4908..7237e1f58 100644 --- a/app/models/card.rb +++ b/app/models/card.rb @@ -29,6 +29,6 @@ class Card < ApplicationRecord end def cache_key - [ super, collection&.name ].compact.join("/") + [ super, collection.name ].compact.join("/") end end diff --git a/test/models/card_test.rb b/test/models/card_test.rb index 49678bd0c..93f13e565 100644 --- a/test/models/card_test.rb +++ b/test/models/card_test.rb @@ -120,11 +120,4 @@ class CardTest < ActiveSupport::TestCase assert_includes card.cache_key, ApplicationRecord.current_tenant, "cache key must always include the tenant" end - - test "cache key gracefully handles a nil collection" do - card = cards(:logo) - card.update_column :collection_id, Collection.last.id + 1 - - assert_nothing_raised { card.reload.cache_key } - end end From 89d1b316f083fb4da9a877df263567ea356c0574 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 21 Apr 2025 09:21:55 +0200 Subject: [PATCH 04/13] Name is already schema and UI required And we are not using these validations for display anywhere. --- app/models/collection.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/models/collection.rb b/app/models/collection.rb index 319b2bb7d..5f4a75051 100644 --- a/app/models/collection.rb +++ b/app/models/collection.rb @@ -7,8 +7,6 @@ class Collection < ApplicationRecord has_many :cards, dependent: :destroy has_many :tags, -> { distinct }, through: :cards - validates_presence_of :name - after_save :update_cards_workflow, if: :saved_change_to_workflow_id? scope :alphabetically, -> { order("lower(name)") } From 3e1a2bcc7e468b6e731c6f7e8fb3fdd3e8794f76 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Mon, 21 Apr 2025 09:24:57 +0200 Subject: [PATCH 05/13] Use morphing to replace the card and exclude turbo frames from being replaced Reconnecting the frames result in a visible flickering. This also prevents flickering in the avatars when replacing the card container. --- app/controllers/concerns/card_scoped.rb | 2 +- app/views/cards/_container.html.erb | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/controllers/concerns/card_scoped.rb b/app/controllers/concerns/card_scoped.rb index 2ea4752c4..5438871c1 100644 --- a/app/controllers/concerns/card_scoped.rb +++ b/app/controllers/concerns/card_scoped.rb @@ -15,6 +15,6 @@ module CardScoped end def render_card_replacement - render turbo_stream: turbo_stream.replace([ @card, :card_container ], partial: "cards/container", locals: { card: @card.reload }) + render turbo_stream: turbo_stream.replace([ @card, :card_container ], partial: "cards/container", method: :morph, locals: { card: @card.reload }) end end diff --git a/app/views/cards/_container.html.erb b/app/views/cards/_container.html.erb index a817a21af..9c2d11aaf 100644 --- a/app/views/cards/_container.html.erb +++ b/app/views/cards/_container.html.erb @@ -32,10 +32,9 @@ <% if card.published? %> - <%# FIXME: Let's move this aside outside of the card container section so these frames don't reload/flicker when card is replaced %> <%= render "cards/container/closure", card: card %> From 4b294ccaa6cc3ac07d2a7cad84cb09c0bdbfb3b8 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 21 Apr 2025 13:55:09 +0200 Subject: [PATCH 06/13] No need to run Solid Cache in development --- config/environments/development.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/environments/development.rb b/config/environments/development.rb index 7eba115d0..6a7ef9df3 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -23,7 +23,7 @@ Rails.application.configure do config.action_controller.perform_caching = true config.action_controller.enable_fragment_cache_logging = true - config.cache_store = :solid_cache_store + config.cache_store = :memory_store config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" } From 167563d9f82720a6a70f3d7d6cddb29035afd370 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 21 Apr 2025 14:01:53 +0200 Subject: [PATCH 07/13] Stlye --- config/environments/development.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/config/environments/development.rb b/config/environments/development.rb index 6a7ef9df3..d2aae6589 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -24,9 +24,7 @@ Rails.application.configure do config.action_controller.enable_fragment_cache_logging = true config.cache_store = :memory_store - config.public_file_server.headers = { - "Cache-Control" => "public, max-age=#{2.days.to_i}" - } + config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" } else config.action_controller.perform_caching = true From b6584ed65540b01d61750a14c71caa40b94c800a Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 21 Apr 2025 15:12:57 +0200 Subject: [PATCH 08/13] Seed multiple seed accounts with basic DSL --- db/seeds.rb | 6 ++++++ db/seeds/37signals.rb | 21 +++++++++++++++++++++ db/seeds/helpers.rb | 33 +++++++++++++++++++++++++++++++++ db/seeds/honcho.rb | 22 ++++++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 db/seeds.rb create mode 100644 db/seeds/37signals.rb create mode 100644 db/seeds/helpers.rb create mode 100644 db/seeds/honcho.rb diff --git a/db/seeds.rb b/db/seeds.rb new file mode 100644 index 000000000..065346716 --- /dev/null +++ b/db/seeds.rb @@ -0,0 +1,6 @@ +require_relative "seeds/helpers" +raise "Seeding is just for development" unless Rails.env.development? + +# Seed accounts +seed_account "37signals" +seed_account "honcho" diff --git a/db/seeds/37signals.rb b/db/seeds/37signals.rb new file mode 100644 index 000000000..432030bbc --- /dev/null +++ b/db/seeds/37signals.rb @@ -0,0 +1,21 @@ +create_tenant "37signals" + +david = create_first_run "David Heinemeier Hansson", "david@37signals.com" +login_as david + +jz = create_user "Jason Zimdars", "jz@37signals.com" +kevin = create_user "Kevin Mcconnell", "kevin@37signals.com" + +create_collection("Fizzy", access_to: [ jz, kevin ]).tap do |fizzy| + create_card("Prepare sign-up page", description: "We need to do this before the launch.", collection: fizzy) + + create_card("Prepare sign-up page", description: "We need to do this before the launch.", collection: fizzy).tap do |card| + card.toggle_assignment(kevin) + card.engage + end + + create_card("Plain text mentions", description: "We'll support plain text mentions first.", collection: fizzy).tap do |card| + card.toggle_assignment(david) + card.close + end +end diff --git a/db/seeds/helpers.rb b/db/seeds/helpers.rb new file mode 100644 index 000000000..ecd1184b3 --- /dev/null +++ b/db/seeds/helpers.rb @@ -0,0 +1,33 @@ +def seed_account(name) + print " #{name}…" + elapsed = Benchmark.realtime { require_relative name } + puts " #{elapsed.round(2)} sec" +end + +def create_tenant(name) + ApplicationRecord.destroy_tenant name + ApplicationRecord.create_tenant name + ApplicationRecord.current_tenant = name +end + +def create_first_run(name, email_address, password: "secret123456") + FirstRun.create!(name:, email_address:, password:) +end + +def login_as(user) + Current.session = user.sessions.create +end + +def create_user(name, email_address, password: "secret123456") + User.create!(name:, email_address:, password:) +end + +def create_collection(name, creator: Current.user, all_access: true, access_to: []) + Collection.create!(name:, creator:, all_access:).tap { it.accesses.grant_to(access_to) } +end + +def create_card(title, collection:, description: nil, status: :published, creator: Current.user) + collection.cards.create!(title:, creator:, status:).tap do |card| + card.capture(Comment.new(body: description)) if description + end +end diff --git a/db/seeds/honcho.rb b/db/seeds/honcho.rb new file mode 100644 index 000000000..5a4a3a2e3 --- /dev/null +++ b/db/seeds/honcho.rb @@ -0,0 +1,22 @@ +create_tenant "honcho" + +david = create_first_run "David Heinemeier Hansson", "david@37signals.com" +login_as david + +jz = create_user "Jason Zimdars", "jz@37signals.com" +kevin = create_user "Kevin Mcconnell", "kevin@37signals.com" + +create_collection("Mucho", access_to: [ jz, kevin ]).tap do |fizzy| + create_card("Prepare MUCHO sign-up page", description: "We need to do this before the launch.", collection: fizzy) + + create_card("Prepare MUCHO sign-up page", description: "We need to do this before the launch.", collection: fizzy).tap do |card| + card.toggle_assignment(kevin) + card.engage + end + + create_card("Plain MUCHO text mentions", description: "We'll support plain text mentions first.", collection: fizzy).tap do |card| + card.toggle_assignment(david) + card.close + end +end + From 2bfead03810d97d486847291507dd90df7167dc6 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 21 Apr 2025 15:23:08 +0200 Subject: [PATCH 09/13] Convert to conventional Rails style --- bin/setup | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/bin/setup b/bin/setup index e50c91ec6..ad27b6637 100755 --- a/bin/setup +++ b/bin/setup @@ -1,26 +1,31 @@ -#!/usr/bin/env bash -set -eo pipefail +#!/usr/bin/env ruby +require "fileutils" -# Use application binstubs -export PATH="./bin:$PATH" +APP_ROOT = File.expand_path("..", __dir__) -announce() { - printf "\n--- $@\n" -} +def system!(*args) + system(*args, exception: true) +end -announce "Installing dependencies" -gem install bundler --conservative -bundle check || bundle install +FileUtils.chdir APP_ROOT do + # This script is a way to set up or update your development environment automatically. + # This script is idempotent, so that you can run it at any time and get an expectable outcome. + # Add necessary setup steps to this file. -announce "Preparing database" -if [[ $* == *--reset* ]]; then - rails db:reset -else - rails db:prepare -fi + puts "== Installing dependencies ==" + system("gem install bundler --conservative") + system("bundle check") || system!("bundle install") -announce "Removing old logs and tempfiles" -rails log:clear tmp:clear + puts "\n== Preparing database ==" + system! "bin/rails db:prepare" + system! "bin/rails db:reset" if ARGV.include?("--reset") -echo -echo "Start developing with bin/dev" + puts "\n== Removing old logs and tempfiles ==" + system! "bin/rails log:clear tmp:clear" + + unless ARGV.include?("--skip-server") + puts "\n== Starting development server ==" + STDOUT.flush # flush the output before exec(2) so that it displays + exec "bin/dev" + end +end From dc127511925ad2db485694dfdef20cef91b6b793 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 21 Apr 2025 15:23:18 +0200 Subject: [PATCH 10/13] Have an empty first-run tenant too --- db/seeds.rb | 1 + db/seeds/first-run.rb | 1 + 2 files changed, 2 insertions(+) create mode 100644 db/seeds/first-run.rb diff --git a/db/seeds.rb b/db/seeds.rb index 065346716..1f1b2322d 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -4,3 +4,4 @@ raise "Seeding is just for development" unless Rails.env.development? # Seed accounts seed_account "37signals" seed_account "honcho" +seed_account "first-run" diff --git a/db/seeds/first-run.rb b/db/seeds/first-run.rb new file mode 100644 index 000000000..e6df219d9 --- /dev/null +++ b/db/seeds/first-run.rb @@ -0,0 +1 @@ +create_tenant "first-run" From 29948d8881184867eadb6ae2cd92ebd6ce753810 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 21 Apr 2025 15:23:31 +0200 Subject: [PATCH 11/13] Enumerate all the options for logging in --- bin/dev | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/dev b/bin/dev index f8d3f3df9..e58bc97c3 100755 --- a/bin/dev +++ b/bin/dev @@ -1,5 +1,7 @@ #!/usr/bin/env sh -echo "Starting Fizzy on http://development-tenant.fizzy.localhost:3006" +echo "Access with david@37signals.com / secret123456 on http://37signals.fizzy.localhost:3006" +echo "Access with david@37signals.com / secret123456 on http://honcho.fizzy.localhost:3006" +echo "Access first run on http://first-run.fizzy.localhost:3006" exec ./bin/rails server -p 3006 From e1b2b623e8869a307b4c4fea5d6c28cad11e7b3d Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 21 Apr 2025 15:26:19 +0200 Subject: [PATCH 12/13] --skip-server in ci setup --- config/ci.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/ci.rb b/config/ci.rb index 1a22e3e33..c94990581 100644 --- a/config/ci.rb +++ b/config/ci.rb @@ -1,7 +1,7 @@ # Run using bin/ci CI.run do - step "Setup", "bin/setup" + step "Setup", "bin/setup --skip-server" step "Style: Ruby", "bin/rubocop" From 581070b22cb205c4b3f8c318456a4e760866740c Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 21 Apr 2025 15:26:47 +0200 Subject: [PATCH 13/13] Fix trailing empty line --- db/seeds/honcho.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/db/seeds/honcho.rb b/db/seeds/honcho.rb index 5a4a3a2e3..918b3a4d5 100644 --- a/db/seeds/honcho.rb +++ b/db/seeds/honcho.rb @@ -19,4 +19,3 @@ create_collection("Mucho", access_to: [ jz, kevin ]).tap do |fizzy| card.close end end -