[
        {
          "id": "ruby-on-rails-git-worktrees-claude-code-ai-2026-02-15-how-to-ruby-on-rails-git-worktrees-and-claude-code",
          "title": "How To: Ruby on Rails, git worktrees and Claude Code",
          "collection": {
            "label": "posts",
            "name": "Posts"
          },
          "categories": "Ruby on Rails, git worktrees, Claude Code, AI",
          "tags": "",
          "url": "/ruby-on-rails/git-worktrees/claude-code/ai/2026/02/15/how-to-ruby-on-rails-git-worktrees-and-claude-code/",
          "content": "Introduction\n\nIf there is one thing which makes coding with AI go from being a helper to being an entire development process, it’s the use of independent workable copies of your code. And what I mean by that is: being able to work on multiple features or ideas at once, independently, without breaking anything — because your environments are so isolated that it’s a breeze to use AI for your agentic development process.\n\nThis is a How To guide where I show you how I use git worktrees and isolated Ruby on Rails environments to enable independent coding using Claude Code.\n\nThe required tooling\n\nBefore we start, let me explain what tools are required to set this up:\n\n\n  Git worktrees — linked working directories from the same repo, each on a different branch. They’re not clones — they share the same .git history. If you already have git installed, you already have worktrees. No extra install needed.\n  Ruby on Rails — with a standard development setup (Postgres, Redis, Sidekiq).\n  Postgres &amp; Redis — either installed locally or running via Docker. Both work fine, Docker just makes it easier to keep things tidy.\n  Claude Code — Anthropic’s CLI agent for coding.\n\n\nI assume you’re familiar with Rails development and how to install Claude Code. The new piece here is git worktrees. Think of them like this: instead of cloning the repo twice, you create a second working directory that points at the same repository. Both directories see the same branches, commits and history — but each one has its own checked-out branch and its own working files. That’s the foundation for everything below.\n\nThe changes\n\nThe first change is your mindset. Don’t think of this as simply “feature branches” — think of each worktree as an independent developer working on your project. You’re the orchestrator.\n\nThe most basic way of using worktrees is git worktree add ../my-feature feature-branch. But your codebase isn’t ready for that kind of independence yet. Each worktree needs its own database, its own port, and its own Redis namespace. Let’s fix that.\n\na) ENV-driven database.yml\n\nStrip out all hardcoded database names and make everything environment-driven:\n\ndefault: &amp;default\n  adapter: postgresql\n  encoding: unicode\n  database: \n  username: \n  password: \n  host: \n  port: \n  pool: 5\n\ndevelopment:\n  &lt;&lt;: *default\n\ntest:\n  &lt;&lt;: *default\n\n\nNow each worktree can point at its own database just by setting DATABASE_NAME.\n\nb) Shared Postgres &amp; Redis\n\nAll worktrees share the same Postgres and Redis instances — they just use different database names and Redis DB numbers. If you’re running them locally, you’re already set. If you prefer Docker, here’s a simple docker-compose.yml:\n\nservices:\n  db:\n    image: postgres:17-alpine\n    volumes:\n      - db_data:/var/lib/postgresql/data\n    environment:\n      POSTGRES_USER: postgres\n      POSTGRES_PASSWORD: password\n    ports:\n      - \"${DOCKER_POSTGRES_PORT:-5431}:5432\"\n\n  redis:\n    image: redis:7-alpine\n    command: redis-server\n    ports:\n      - \"${DOCKER_REDIS_PORT:-6380}:6379\"\n\nvolumes:\n  db_data:\n\n\nThe non-default ports (5431/6380) avoid collisions with any locally installed Postgres or Redis. Adjust to match your setup — the important thing is that every worktree talks to the same services but uses a different database name and Redis DB number.\n\nc) Auto-derive environment per worktree\n\nThis is the core trick. A bin/worktree-env script reads the directory name and derives all the environment variables from it. Let’s say your app is called myapp:\n\n#!/usr/bin/env bash\nWORKTREE_NAME=$(basename \"$PWD\")\nWORKTREE_SUFFIX=$(echo \"$WORKTREE_NAME\" | sed 's/^myapp-*//' | sed 's/-/_/g')\n\nif [ -z \"$WORKTREE_SUFFIX\" ] || [ \"$WORKTREE_SUFFIX\" = \"myapp\" ]; then\n  # Main worktree\n  DATABASE_NAME=\"${DATABASE_NAME:-myapp_development}\"\n  PORT=\"${PORT:-3000}\"\n  REDIS_DB=\"${REDIS_DB:-1}\"\nelse\n  # Feature worktree — derive everything from the directory name\n  DATABASE_NAME=\"${DATABASE_NAME:-myapp_${WORKTREE_SUFFIX}_development}\"\n  PORT=$(( 3001 + $(echo \"$WORKTREE_SUFFIX\" | cksum | cut -d' ' -f1) % 99 ))\n  REDIS_DB=$(( $(echo \"$WORKTREE_SUFFIX\" | cksum | cut -d' ' -f1) % 15 + 1 ))\nfi\n\necho \"export DATABASE_NAME='$DATABASE_NAME'\"\necho \"export PORT='$PORT'\"\necho \"export REDIS_URL='redis://localhost:${REDIS_PORT:-6379}/$REDIS_DB'\"\n\n\nDirectory myapp-feature-auth → database myapp_feature_auth_development, port 3042 (or whatever the hash lands on), Redis DB 7. Fully deterministic, no manual config.\n\nd) Procfile.dev\n\nThe Procfile just references ${PORT} so each worktree starts on its own port:\n\nweb: bin/rails server -p ${PORT:-3000}\ncss: bin/rails dartsass:watch\nsidekiq: bundle exec sidekiq -C config/sidekiq.yml\n\n\nThe flow state\n\nOnce you understand the concept, it’s time to automate. I added three scripts to bin/ that handle the full lifecycle:\n\nbin/wt-new — create a new worktree\n\nbin/wt-new feature-auth              # branch off current branch\nbin/wt-new feature-auth develop      # branch off develop\n\n\nWhat it does:\n\n  Creates ../myapp-feature-auth/ as a git worktree on a new branch\n  Generates a .env.local with the derived DATABASE_NAME, PORT and REDIS_URL\n  Runs the full setup: bundle install, yarn install, db:prepare, db:seed\n\n\nYou end up with a ready-to-run Rails environment in seconds.\n\nbin/wt-list — see what’s running\n\n══════════════════════════════════════════════════════════════════\n                          Git Worktrees\n══════════════════════════════════════════════════════════════════\n\n  ● myapp                          :3000  develop\n    myapp-feat-new-folder-design   :3002  feat/new-folder-design\n\n  ● = Running\n\n\nShows every worktree, its port, its branch, and whether it’s currently running.\n\nbin/wt-rm - clean up when you’re done\n\n\n  Drops the worktree’s Postgres database\n  Removes the worktree directory\n  Optionally deletes the branch (with a confirmation prompt)\n\n\nNow here’s where it gets fun. Open a separate terminal in each worktree directory and run claude in each one. Each Claude Code session sees only that worktree’s files and branch. You can have one session implementing a feature, another fixing a bug, and a third refactoring tests - all at the same time, all completely isolated. You review and merge when each one is done.\n\nThat’s the flow state: you stop being the person who writes every line and start being the person who directs the work and reviews the output.\n\nConclusion\n\nAnd that’s it. That’s the magic. It’s amazing what you can achieve with this setup. You are much faster and can work on multiple features at once. Or review code while a big feature is churning away in another worktree. It’s proper development orchestration.\n\nJust make sure to have proper safeguards in place to not produce code which doesn’t do what you want it to do. But I will talk about that in another article in the future.\n\nCheers,\nMichael"
        },
        {
          "id": "tiling-window-manager-linux-macos-2026-02-14-a-modern-tiling-manager-for-macos-26",
          "title": "The most efficient way to use your MacBook",
          "collection": {
            "label": "posts",
            "name": "Posts"
          },
          "categories": "Tiling Window Manager, Linux, macOS",
          "tags": "",
          "url": "/tiling-window-manager/linux/macos/2026/02/14/a-modern-tiling-manager-for-macos-26/",
          "content": "Introduction\n\nMy computer usage history is quite diverse. As any 90s kid, I started off — of course — with Windows. The first experience using a computer was at my cousin’s place using his Windows 98 and then Windows 2000 setup. I felt right at home — it was exciting to use a computer and I was hooked. Once I convinced my parents to get my own PC, Windows XP was already out and therefore I consider Windows XP as my first true computing experience.\n\nInitially without Internet, Minesweeper, Ping Pong and store bought games and Paint filled all my computing needs. I was spending quite some time playing games. I enjoyed Stronghold, Age of Empires and Tony Hawk.\n\nFast forward to Windows 7 — a more polished operating system arrived — and I loved it. It felt modern to me. I grew up with it and it was my main operating system until I started with university where the need for a laptop arose. I started my computer science studies with a switch to macOS and bought myself a new MacBook Air 13.3 running macOS Mountain Lion. I loved the experience.\n\nThe Big Change\n\nI noticed something during the beginning of university: There were three types of people. The Windows kids, the Apple fanboys and the Linux nerds. And as I studied computer science, the split was more like 10% Windows, 50% Linux and 40% macOS. And my curiosity kicked in. What is Linux like? Why is everyone using it? And why on earth is everyone running Linux on these office laptops called ThinkPads. But I got curious and wanted to see what the experience was. And so I bought myself an X240 ThinkPad and installed Arch Linux on it. Yes, you read that right. I didn’t start with Ubuntu, I went full nerd and installed Arch.\n\nAdditionally to Arch, I found the unixporn subreddit and the concept of a tiling window manager was introduced to me. Therefore, I went and installed i3wm and I loved it. Once I played around I started to use this system more and more in daily tasks and kind of switched over from my MacBook. Eventually, I bought myself an X1 Carbon 14inch and used Linux as my main operating system for 1,5 years.\n\nThe Switchback\n\nDue to work reasons and the interest in mobile app engineering, I switched back to a MacBook in early 2019. My love for Linux is still there and I kept my X240 as a “tinkering device”. I’ve used 15inch MacBook Pros and then a 14inch MacBook Pro and recently a MacBook Pro 16 inch with an M2 Processor. The main reason for the 16inch device is that I’m constantly on the road and I need the screen real estate to work efficiently. 14inch was too small and felt slightly cramped to work with. But I was missing the ease of use of a tiling window manager, especially when you are working only on a laptop without an external monitor. And then I stumbled upon something magical for macOS.\n\nAeroSpace — The Missing Tiling Window Manager for macOS\n\nAeroSpace is a tiling window manager for macOS inspired by i3. It came into my life in the middle of 2025 and it felt like I finally got what I wanted: macOS merged with i3-like window tiling.\n\nFor work on the laptop it’s a godsend. Once you get used to working with a tiling window manager (I was already due to my time with i3) it’s like a cheat code. Navigating around macOS feels so much quicker and snappier and I don’t have to move my hands to the trackpad as often. It feels like a 50% speed improvement over regular swiping navigation.\n\nTogether with a quick app launcher like Raycast working on a laptop is extremely efficient. It’s so efficient that I also switched to working on a single monitor on my desk as I feel like it’s much better and I can focus better on tasks when having quick access to everything with a keyboard shortcut away.\n\nConclusion\n\nGo try it out, it’s magic. Window tiling managers are your cheat code to a much more efficient and speedy navigation on macOS. It’s extremely addictive and I’d love for everyone to experience it. But I get that it’s nerdy and not for everyone, so take my recommendation with a grain of salt.\n\nCheers, Michael"
        },
        {
          "id": "ruby-rails-bootstrap-2024-01-27-dynamic-search-rails",
          "title": "Implementing Dynamic Search with Turbo Frames in Ruby on Rails 7",
          "collection": {
            "label": "posts",
            "name": "Posts"
          },
          "categories": "Ruby, Rails, Bootstrap",
          "tags": "",
          "url": "/ruby/rails/bootstrap/2024/01/27/dynamic-search-rails/",
          "content": "Introduction\n\nIn web development, a responsive and efficient search experience is key to user satisfaction. For my recent project at AVIMBU, I implemented a search feature that queries both local data and external APIs. This was achieved using Hotwire’s TurboFrames in a Ruby on Rails 7 web application.\n\nIn this blog post, I’ll guide you through implementing a dynamic search bar that fetches data both locally and from an external source (such as an API), all without a full page reload thanks to TurboFrames.\n\nSection 1: Initial Project Setup\n\nAs we are building a dynamic search implementation for a Ruby on Rails 7 project, we need a few steps to setup the initial project state. For this, run the following command:\n\nrails new dynamic_search --css=bootstrap -j=esbuild\n\n\nThis command creates a Rails project with the latest version of Bootstrap pre-configured (this is optional but provides better styling). For JavaScript packaging, we will use esbuild, which is straightforward and simple to use. You could also opt for using webpacker or the more modern import_maps approach, however, for now it’s easiest to stick with esbuild due to its straightforward integration of Bootstrap.\n\nAfter project creation, navigate to the project directory and start it with:\n\nbin/dev \n\n\nYour application should now be available at localhost:3000.\n\nThis setup includes Turbo by default, a key component for dynamic page updates. Confirm its inclusion in app/javascript/application.js:\n\nimport \"@hotwired/turbo-rails\"\n\n\nSection 2: Implementing the Search Feature\n\nBegin by creating a Project model with name and description attributes. Use Rails’ scaffolding to quickly set up the model and its CRUD operations:\n\nrails g scaffold project name description\n\nOnce that’s done, you should be able to create, update and delete some project model entities within your application.\n\nLet’s add the search function: I’ve chosen the index action of the ProjectsController. Therefore I’ve added a search form to the corresponding view template:\n\n&lt;%= form_with url: projects_path, method: :get, data: { turbo_frame: 'project_listings' } do |form| %&gt;\n  &lt;%= form.search_field :q, class: 'form-control', placeholder: \"Search for Name or Description\" %&gt;\n&lt;% end %&gt;\n\n\nThis adds a simple search bar which allows us to submit a GET search request against the projects#index action. The search query is provided via the q parameter. I’ve adapted the index action as follows:\n\ndef index\n@projects = Project.all\n@search = params[:q] \n@results = if @search.blank?\n  []\nelse\n  Project.where('name LIKE :search OR description LIKE :search', search: \"%#{@search}%\")\nend\nend\n\n\nAfter this change, I have two new variables which are accessible within the index view. @search holds the search query and @results holds an array of found Project models. For the sake of demonstration, I’ve implemented the database search using a simple LIKE based search query. This can and should be improved by using better implementations such as pg_search.\n\nThe app/views/projects/index.html.erb also needs some changes:\n\n&lt;%= turbo_frame_tag 'project_listings' do %&gt;\n  &lt;% unless @results.empty? %&gt;\n      &lt;% @results&amp;.each do |project| %&gt;\n          &lt;div class=\"p-2 border\"&gt;\n              &lt;div class=\"fs-6\"&gt;Name: &lt;%= project.name %&gt; - Description: &lt;%= project.description %&gt;&lt;/div&gt;\n          &lt;/div&gt;\n      &lt;% end %&gt;\n  &lt;% end %&gt;\n&lt;% end %&gt;\n\nI’ve wrapped the search results in a turbo_frame_tag with the same ID as the form submits to. Therefore, Turbo is smart enough to figure out which part of the DOM needs to change and only updates the project_listings turbo_frame_tag to avoid a full page refresh.\n\nNo, if the local DB search returns some results, the search results are shown without the need of a full page refresh.\n\nSection 3: Integration of External Search\n\nThe first search part is now implemented. Now, let’s add some external search functionality to incorporate an external API for my search results in case the local search doesn’t provide the results I’m looking for. For this, I’m using the concept of lazy-loading turbo_frame_tags. This allows me to trigger / load the source action of a TurboFrame as soon as the frame is visible in the viewport.\n\nI have to change three things to make it work. First, add the turbo_frame_tag within the first search result TurboFrame s.t. it’s always reloaded when the first TurboFrame is reloaded. Ensure that the src option is pointing to the external search action, which we are now going to add. For this, add the following route to your routes.rb file:\n\nget 'projects/external_search_project' =&gt; 'projects#external_search_project', as: :load_external_search_project\n\n\nAlso, add this method (action) to the projects controller:\n\ndef external_search_project\n  return if params[:q].blank? || params[:avoid_search] == \"true\"\n\n  sleep 1.0 # simulate external request\n  @external_search_result = [Project.new(name: \"External Project\", description: \"Loaded from wherever\")]\nend\n\n\nIt’s an example implementation where the external search request is simulated using sleep. Also, the action is skipped if no search query is passed or an avoid_search param is given.\n\nFinally, add the external search TurboFrame inside the first TurboFrame to ensure that it’s always loaded with a regular search update. By passing the search query and setting the avoid_search parameter depending on whether local search results are available or not, the external search is only triggered accordingly. This allows for only executing and rendering external search results if local search didn’t provide any results.\n\n&lt;%= turbo_frame_tag 'project_listings' do %&gt;\n  &lt;% unless @results.empty? %&gt;\n      &lt;% @results&amp;.each do |project| %&gt;\n          &lt;div class=\"p-2 border\"&gt;\n              &lt;div class=\"fs-6\"&gt;Name: &lt;%= project.name %&gt; - Description: &lt;%= project.description %&gt;&lt;/div&gt;\n          &lt;/div&gt;\n      &lt;% end %&gt;\n  &lt;% end %&gt;\n  &lt;%= turbo_frame_tag 'external_search_result', src: load_external_search_project_path(q: @search, avoid_search: @results.any?), loading: :lazy do %&gt;\n  &lt;% end %&gt;\n&lt;% end %&gt;\n\n\nAlso, to update the external_search_result TurboFrame, add this view template to app/views/projects/external_search_project.html.erb. This will be rendered on external search and lists the external search results if they are available.\n\n&lt;%= turbo_frame_tag \"external_search_result\" do %&gt;\n&lt;% @external_search_result&amp;.each do |project| %&gt;\n  &lt;div class=\"p-2 border\"&gt;\n    &lt;div class=\"fs-6\"&gt;Name: &lt;%= project.name %&gt; - Description: &lt;%= project.description %&gt;&lt;/div&gt;\n  &lt;/div&gt;\n&lt;% end %&gt;\n&lt;% end %&gt;\n\n\nThat’s it. Now you have control over your search behavior. This allows you to adapt the search implementation to your needs.\n\nThe final product should look like this:\n\n\n\nSection 4: Improvement: Form Auto-Submission\n\nEnhance the search experience with automatic form submission. Use a Stimulus controller to submit the form after a brief delay. Attach the controller to the form as follows:\n\nimport { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n\nsearch() {\n  clearTimeout(this.timeout)\n  this.timeout = setTimeout(() =&gt; {\n    this.element.requestSubmit()\n  }, 300)\n}\n}\n\nYou also need to attach this controller to the search form like this:\n\n&lt;%= form_with url: projects_path, method: :get, data: { controller: \"formsubmission\", turbo_frame: 'project_listings' } do |form| %&gt;\n  &lt;%= form.search_field :q, class: 'form-control', placeholder: \"Search for Name or Description\", data: { action: \"input-&gt;formsubmission#search\" } %&gt;\n&lt;% end %&gt;\n\n\nNow, after this addition, the form submits automatically after 300ms.\n\nConclusion\n\nUtilizing TurboFrames in this manner provides an effective solution for dynamic searching in Rails applications.\n\nAdditional Resources\n\nFor more insights into TurboFrames and Hotwire, visit the Hotwire Turbo documentation.\n\nIf you want to know more about me, feel free to check out my homepage or follow me on Twitter here.\n\nAdditional Resources\n\nThe code used in this tutorial can be found here.\n\nAlternative resources:\n\n\n  Ruby on Rails Guidelines\n  Rails Hotwire"
        },
        {
          "id": "ruby-rails-bootstrap-2024-01-25-bootstrap-5-modals",
          "title": "Integrating Bootstrap 5 Modals with Ruby on Rails 7 Using Turbo Frames",
          "collection": {
            "label": "posts",
            "name": "Posts"
          },
          "categories": "Ruby, Rails, Bootstrap",
          "tags": "",
          "url": "/ruby/rails/bootstrap/2024/01/25/bootstrap-5-modals/",
          "content": "Introduction\n\nIn my journey to build a new Ruby on Rails 7 app, I aim to create something as modern and reactive as a Single Page Application (SPA) written in React. The project I’m currently working on, named AVIMBU, can be explored further here.\n\nA common feature in SPAs is the use of modals. My goal was to find a simple yet powerful and flexible approach to integrate Bootstrap modals without adding them permanently to the DOM, only revealing them upon a button click. Fortunately, Turbo Frames offer a solution to transfer HTML over the wire, allowing us to display modals without rerendering the entire page.\n\nThis blog post provides a step-by-step guide on integrating a Bootstrap 5 modal in a Ruby on Rails 7 application using Turbo Frames, thus enhancing your application’s interactivity without full page reloads.\n\n\n\nRuby On Rails\n\nSection 1: Initial Project Setup\n\nIn order to create a barebones Rails 7 project with Bootstrap preconfigured, run the following command:\n\nrails new bootstrap_modal --css=bootstrap -j=esbuild\n\n\nThis command creates a Rails project with the latest version of Bootstrap preconfigured. For JavaScript packaging, we will use esbuild, which is straightforward and simple to use. You could also opt for using webpacker or the more modern import_maps approach, however, for now it’s easiest to stick with esbuild due to its straightforward integration of Bootstrap.\n\nAfter project creation, navigate to the project directory and start it with:\n\nbin/dev \n\n\nYour application should now be available at localhost:3000.\n\nThis initial setup comes with Turbo enabled. Verify this by checking for the following line in app/javascript/application.js:\n\nimport \"@hotwired/turbo-rails\"\n\n\nSection 2: Add a Turbo Frame Tag\n\nIn order to achieve a reactive and modern-feeling web application, we’ll utilize Turbo Frames. Turbo Frames, a key component of Hotwire, allow us to define which parts of an application’s HTML should be replaced, rather than doing a full page reload.\n\nTo use them, add the following line to a view file:\n\n&lt;%= turbo_frame_tag \"modal\", target: \"_top\" %&gt;\n\n\nI added this line to my application.html.erb file, right above the &lt;%= yield %&gt; part. This allows for adding my modal HTML element to this frame. The turbo_frame_tag creates a div element with the class set to modal, which Turbo uses to replace the content of this div with the new Turbo Frame content. The target: \"_top\" attribute allows for conventional redirects (e.g., on model creation).\n\nSection 3: Add a Modal Partial\n\nFrom the Bootstrap documentation, you can find some skeleton Bootstrap 5 modal examples here. I used one of them and created a _modal.html.erb partial for later reuse. Ensure to copy an “active” modal instead of a hidden one. The partial looks like this:\n\n&lt;%= turbo_frame_tag \"modal\" do %&gt;\n  &lt;div class=\"modal fade\" tabindex=\"-1\" data-controller=\"modal\"&gt;\n    &lt;div class=\"modal-dialog modal-dialog-centered modal-lg\"&gt;\n      &lt;div class=\"modal-content\"&gt;\n        &lt;div class=\"modal-header\"&gt;\n          &lt;div class= \"modal-title fs-3\"&gt;\n            &lt;%= title %&gt;\n          &lt;/div&gt;\n          &lt;button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"&gt;&lt;/button&gt;\n        &lt;/div&gt;\n        &lt;div class=\"modal-body\" id=\"remote_modal_body\" &gt;\n          &lt;%= yield %&gt;\n        &lt;/div&gt;\n      &lt;/div&gt;\n    &lt;/div&gt;\n  &lt;/div&gt;\n&lt;% end %&gt;\n\n\nSection 4: Stimulus Controller for Background Handling\n\nSince the Bootstrap modal relies on some JavaScript logic, I added a few lines of JavaScript to ensure proper background handling. For this, I chose to use a Stimulus controller as it’s the easiest solution for handling JavaScript within a modern Rails 7 application with Turbo. The controller looks like this:\n\nimport { Controller } from \"@hotwired/stimulus\"\nimport { Modal } from \"bootstrap\";\n\n// Connects to data-controller=\"modal\"\nexport default class extends Controller {\n  connect() {\n    let backdrop = document.querySelector(\".modal-backdrop\");\n    if (backdrop) {\n      backdrop.remove();\n    }\n    this.modal = new Modal(this.element);\n    this.modal.show();\n    this.element.addEventListener(\"hidden.bs.modal\", (event) =&gt; {\n      this.element.remove();\n    });\n  }\n}\n\n\nAs seen in my _modal.html.erb partial, I’ve added the data-controller=\"modal\" parameter to link this Stimulus controller with the modal HTML. This controller ensures that the modal backdrop is handled correctly by properly removing and showing the Modal element.\n\nSection 5: Adapt Controller Actions to Use Turbo Frames\n\nHere’s the crucial part of integrating Turbo Frames into our Controller actions. I created a simple example scaffold for some model logic:\n\nrails g scaffold project name description\n\n\nThis command creates all necessary controllers, models, and views to create, update, or delete Project models. Change your root route to the index action of the Project controller by adding this to routes.rb:\n\nroot \"projects#index\"\n\n\nNow, when you open your application, you’ll see the index page of the Project model, allowing you to create some Project models. However, the new and edit actions redirect to their respective .html.erb pages. We want them to open within a modal. This can be done by adapting the view files as follows:\n\n&lt;%= render 'partials/modal', title: \"New Project\", fade_in: true do %&gt;\n  &lt;%= render \"form\", project: @project %&gt;\n&lt;% end %&gt;\n\n\nDo the same for the edit.html.erb file. Now, when you try to create or edit a model, the view renders within a Bootstrap modal 🥳\n\nSection 6: Add Form Error Handling\n\nIf you add model validations, such as requiring a name, you might notice that errors are not properly handled within the modal.\n\nclass Project &lt; ApplicationRecord  \n  validates :name, presence: true  \nend\n\n\nIn order to improve this, we need to change how the controller responds in case of an error for both the create and update actions:\n\n# POST /projects or /projects.json  \ndef create  \n  @project = Project.new(project_params)  \n  \n  if @project.save  \n    redirect_to project_url(@project), notice: \"Project was successfully created.\"  \n  else  \n    render :form_update, status: :unprocessable_entity  \n  end  \nend  \n  \n# PATCH/PUT /projects/1 or /projects/1.json  \ndef update  \n  if @project.update(project_params)  \n    redirect_to project_url(@project), notice: \"Project was successfully updated.\"  \n  else  \n    render :form_update, status: :unprocessable_entity  \n  end  \nend\n\n\nThis adds a custom render response for error cases, instructing the controller to render the form_update view. This view will update the modal’s body via a Turbo Stream (see Turbo Streams documentation) to show the correct error and avoid a full page reload. The form_update.turbo_stream.erb file looks like this:\n\n&lt;%= turbo_stream.update \"remote_modal_body\" do %&gt;\n  &lt;%= render \"form\", project: @project %&gt;\n&lt;% end %&gt;\n\n\nNow, if you try to save a Project model without a name, the error is shown within the modal:\n\n\nModal with Error\n\nThat’s it! We’ve successfully implemented a Bootstrap modal using Turbo Frames, making our Rails 7 application feel more responsive and modern.\n\nConclusion\n\nThe final product should look like this:\n\n\nFinal Product\n\nWe’ve covered how to integrate a Bootstrap 5 modal in a Ruby on Rails 7 application using Turbo Frames. This integration allows for a more dynamic and responsive user interface without the need for full page reloads. Experiment with Turbo Frames and Bootstrap modals, and feel free to share your experiences or ask questions in the comments below.\n\nIf you want to know more about me, feel free to check out my homepage or follow me on Twitter here.\n\n\nBootstrap 5 - a developer friendly CSS component library\n\nAdditional Resources\n\nThe code used in this tutorial can be found here.\n\nAlternative resources:\n\n\n  Ruby on Rails Guidelines\n  Bootstrap documentation\n  Rails Hotwire"
        },
        {
          "id": "platform-as-a-rails-bootstrap-2023-08-20-dokku-setup",
          "title": "Setup Dokku (a free Heroku alternative) and Deploy a Ruby on Rails 7 Application",
          "collection": {
            "label": "posts",
            "name": "Posts"
          },
          "categories": "Platform as a, Rails, Bootstrap",
          "tags": "",
          "url": "/platform-as-a/rails/bootstrap/2023/08/20/dokku-setup/",
          "content": "Create your own Heroku clone by using Dokku\n\nHeroku was a popular solution for quick and easy deployments for Ruby on Rails developers. It was handy for testing and learning purposes with its free tier option, offering Platform as a Service (PaaS) solutions for everyone. Unfortunately, Heroku dropped its free tier solution, making it necessary to pay for every service offered by Heroku. On the hunt for a new solution to quickly and easily deploy Ruby on Rails applications, I found a Heroku alternative called Dokku.\n\nDokku is a free open-source Platform as a Service solution that allows you to build your own Heroku clone on your server. It allows you to do git push-based deployments directly to your own Dokku instance running on your server with useful auto-deployment features. Dokku supports pretty much anything which could have been deployed to Heroku.\n\nThe main use case I’m covering with Dokku is for testing and development, however, I don’t see any problems running smaller applications also in production using Dokku.\n\nThis guide shows you how to set up Dokku on your server and explains how to deploy and configure a Ruby on Rails 7 application (including Postgres, Let’s Encrypt and Database Encryption) as well as how to work with your application (executing commands, logging and configuration).\n\nPrerequisites\n\nWhat do you need?\n\n\n  A VPS (Virtual Private Server) or dedicated server with a fresh Ubuntu 20.04 installation with at least 1GB of RAM.\n  (Optional) Domain name pointing to your server’s IP address.\n  Basic familiarity with the Linux command line as well as SSH.\n\n\nPart 1 - Install and Setup Dokku\n\nThe first part of this article explains how to install and set up Dokku on your server. For this, the following steps have to be executed:\n\n\n  SSH into your server as the root user:\n\n\n  ssh root@your\\_server\\_ip\n\n\n\n  Download and run the Dokku installation script:\n\n\n  wget -NP . https://dokku.com/install/v0.31.0/bootstrap.sh  \n  sudo DOKKU\\_TAG=v0.31.0 bash bootstrap.sh\n\n\n\n  Add your domain to Dokku (“acme.com” is your domain). You also have to add an A or CNAME record in your domain’s DNS settings pointing to your server’s IP address.\n\n\n  dokku domains:set-global acme.com\n\n\n\n  Alternatively, you can just use your server’s IP address directly:\n\n\n  dokku domains:set-global &lt;your servers IP address&gt;  \n  dokku domains:set-global &lt;your servers IP address&gt; # add `.sslip.io` for subdomain support\n\n\nThat’s pretty much it for the Dokku setup. After these steps, your Dokku instance is ready for its first deployment. If you need more instructions, check their excellent documentation here.\n\nPart 2 - Ruby on Rails 7 Application Deployment\n\nYou can skip the first two steps if you already have an existing application. If not, follow along.\n\n\n  Create a new Ruby on Rails application (it’s assumed that you have your system setup for Rails 7 development):\n\n\n  rails new myapp &amp;&amp; cd myapp\n\n\n\n  Create your first commit\n\n\n  git add .   \n  git commit -m \"chore: Initial commit\"\n\n\n\n  To deploy the application to your Dokku server on git push, you simply need to add your server as a new remote git server. This can be done by running the following command:\n\n\n  git remote add dokku dokku@&lt;global dokku domain or IP address&gt;:myapp\n\n\n\n  After this, you can simply push your application to your Dokku instance:\n\n\n  git push dokku master\n\n\n\n  By doing this, Dokku will automatically detect that you are pushing a Ruby on Rails application and start the deployment process.\n\n\nAfter a successful deployment, you can access your Rails application by visiting: http://your-domain.com. Dokku also prints the deployment log to your terminal so you can follow along. In the end, the URL of your deployed application is printed for you to access your application.\n\nPart 3 - Database Configuration\n\nTo get your database set up correctly the following steps are necessary. For the sake of this tutorial, it’s assumed that you use Postgres as your production database.\n\n\n  Update your database configuration in your Ruby on Rails project. Dokku exports a DATABASE_URL environment variable which you can use to set up the configuration properly. Therefore, adapt your “database.yml” configuration file with the following lines:\n\n\n  production:  \n    adapter: postgresql  \n    encoding: unicode  \n    url:   \n    pool: 5\n\n\n\n  Next, SSH into your Dokku server and install Postgres plugin for Dokku on your server:\n\n\n  dokku plugin:install https://github.com/dokku/dokku-postgres.git\n\n\n\n  After that, you can simply create and link a new Postgres database by running this:\n\n\n  dokku postgres:create mydatabase  \n  dokku postgres:link mydatabase myapp\n\n\n\n  Now your application deployment can talk to your Postgres database. The linking command automatically restarts the deployment for you s.t. the Rails application is correctly configured.\n\n\nAdditionally, if you work with the Rails migration system, it’s wise to set up an automatic migration command for each deployment. For this, add an “app.json” file to your Rails project’s root path, containing the following lines:\n\n{  \n\"name\": \"&lt;Name of Application&gt;\",  \n\"description\": \"&lt;Description of your application&gt;\",  \n\"scripts\": {  \n  \"dokku\": {  \n    \"postdeploy\": \"bundle exec rake db:migrate\"  \n  }  \n}  \n}\n\n\nThis will automatically execute the migration command after every deployment. You can also configure any other command you might need here. Commit and push this file and your deployment should execute the migration automatically.\n\nIf you want to use Rails Active Record Encryption, you need to configure your Rails deployment with the “RAILS_MASTER_KEY”, containing the content of the “master.key” file. This can be done by adding the environment variable to the deployment by using the following Dokku command:\n\ndokku config:set &lt;name-of-application&gt; RAILS\\_MASTER\\_KEY=&lt;content of master.key&gt;  \ndokku config:show &lt;name-of-application&gt; # shows all environment variables set for the application\n\n\nWith this command, it’s also possible to set up any other environment variable you might need in production.\n\nPart 4 - Redis\n\nIf your Rails application uses Redis as a cache or session store, Dokku offers a solution for that. As with the Postgres setup, you need to install a plugin and configure some environment variables:\n\n\n  SSH into your server and install the Redis plugin\n\n\n  dokku plugin:install https://github.com/dokku/dokku-redis.git redis \n\n\n\n  Create a Redis instance and link it to your application by running:\n\n\n  dokku redis:create myredis  \n  dokku redis:link myredis myapp\n\n\n\n  The Redis plugin exposes a “REDIS_URL” environment variable to add to your production Redis configuration. Add the environment variable so your Rails application knows where to find your Redis instance. Update your application configuration accordingly and push the changes to Dokku. Your production deployment should pick up the connection to the Redis instance automatically and work as expected.\n\n\nPart 5— SSL &amp; Let’s Encrypt Setup\n\nIf you need SSL for your application, Dokku has a Let’s Encrypt plugin you can easily integrate. For this, the following steps are necessary.\n\n\n  SSH into your server and install the plugin by running this command:\n\n\n  dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git \n\n\n\n  Configure your application with the Let’s Encrypt email:\n\n\n  dokku config:set --no-restart &lt;appname&gt; DOKKU\\_LETSENCRYPT\\_EMAIL=your@email.tld\n\n\n\n  Add the plugin to your application\n\n\n  dokku letsencrypt myapp\n\n\n\n  That should be all. If you want auto-renewal of your certificates, you can add an easy cron-job script with the following command:\n\n\n  dokku letsencrypt:cron-job --add myapp\n\n\nNow you are all set and your application should be accessible via HTTPS.\n\nTips and Tricks\n\n\n  If you want to avoid the SSH connection to your server, Dokku offers the option to run your remote commands from your local command line. For this, you need to install the official Dokku client and configure it with your server’s connection information. For this, see the following guide.\n  I encountered a problem with the first deployment of my Ruby on Rails application with a mismatch of the bundler version. This is and was a common problem also for Heroku. You can simply fix this by updating your local bundler version and reinstalling bundler. See this Stackoverflow thread for more information: Link.\n  Dokku recommends at least 1GB of RAM for your server infrastructure. From personal experience, if you have a smaller Rails application with a DB connection I recommend at least 4GB of RAM. This ensures proper performance.\n\n\nConclusion\n\nThe experience of setting up Dokku and deploying a Ruby on Rails 7 application is pretty seamless. It offers everything you expect from a PaaS solution and can be easily configured and extended to your application needs. This tutorial covers the basic configuration as well as the deployment instructions necessary.\n\nFor further information, check the reference links down below.\n\nHappy Coding\n\nMichael\n\nThanks for reading this article. If you want, follow me on Twitter.\n\nRelated Links\n\n\n  https://dokku.com/docs/getting-started/installation/\n  https://github.com/dokku/dokku-letsencrypt\n  https://github.com/dokku/dokku-postgres\n  https://github.com/dokku/dokku-redis\n  https://guides.rubyonrails.org/getting_started.html"
        },
        {
          "id": "404",
          "title": "404",
          "collection": {
            "label": "pages",
            "name": "Posts"
          },
          "categories": "",
          "tags": "",
          "url": "/404",
          "content": "404\n\nPage Not Found :(\n\nThe requested page could not be found."
        },
        {
          "id": "500",
          "title": "500",
          "collection": {
            "label": "pages",
            "name": "Posts"
          },
          "categories": "",
          "tags": "",
          "url": "/500",
          "content": "500\n\nInternal Server Error :(\n\nThe requested page could not be delivered."
        },
        {
          "id": "about",
          "title": "About",
          "collection": {
            "label": "pages",
            "name": "Posts"
          },
          "categories": "",
          "tags": "",
          "url": "/about/",
          "content": "I’m Michael Bauer-Wapp, an independent software developer from Austria. I work fully remotely.\n\nMy interest in computers started when I was quite young. I’ve always liked to tinker around and was the person in the family known for being good with these new technologies.\n\nThroughout my school years, I was more interested in maths and computers than in languages or other subjects. I was on the fence between studying math or computer science, but ultimately decided to take the programming route. I started programming before university and have continued ever since.\n\nAfter completing my Master’s degree in computer science at the Technical University of Vienna, I worked at various companies before finally deciding to switch from full-time employment to becoming an independent software developer, working with various businesses along the way.\n\nMy passion lies in being a professional partner for people looking to build software projects. I have a business-forward mindset and understand the needs and tradeoffs necessary to build a company. I help guide and navigate the technical challenges along the way, and I will happily make sure your software is capable of achieving what you require from it.\n\nNowadays, I specialize in modern web development, mostly doing Ruby on Rails and Next.js work.\n\nIf you ever need a reliable and highly skilled partner for your software project, I’m here to help.\n\nMichael"
        },
        {
          "id": "imprint",
          "title": "Imprint | Impressum",
          "collection": {
            "label": "pages",
            "name": "Posts"
          },
          "categories": "",
          "tags": "",
          "url": "/imprint/",
          "content": "Business Name: Michael Bauer-Wapp\n\nAddress: Dr. Karl Rennerstraße 52, 7000 Eisenstadt\n\nContact: info@michaelwapp.com\n\nUnternehmensgegenstand: Software Development and IT Services  (Dienstleistungen in der automatischen Datenverarbeitung und Informationstechnik)\n\nVAT (UID):ATU79489028\n\nName of Owner: Michael Bauer-Wapp (Member of WKO)\n\n\n\nLIABILITY FOR THE CONTENT OF THIS WEBSITE\n\nI continuously develop the content of this website and strive to provide correct and up-to-date information. Unfortunately, I cannot accept any liability for the correctness of all content on this website, especially for content provided by third parties.\n\nShould you notice any problematic or unlawful content, please contact me immediately. You can find the contact details in the imprint.\n\nLIABILITY FOR LINKS ON THIS WEBSITE\n\nMy website contains links to other websites for whose content I am not responsible. According to § 17 ECG (Austrian E-Commerce Act), I bear no liability for linked websites, as I had and have no knowledge of any unlawful activities, no such illegalities have come to my attention, and I would remove links immediately if I became aware of any unlawful content.\n\nIf you notice any unlawful links on my website, please contact me. You can find the contact details in the imprint.\n\nCOPYRIGHT NOTICE\n\nAll content on this website (images, photos, texts, videos) is subject to copyright. If necessary, I will take legal action against the unauthorized use of parts of my website’s content.\n\nIMAGE CREDITS\n\nThe images, photos, and graphics on this website are protected by copyright.\n\nThe image rights belong to the following photographers and companies:\n\nMichael Bauer-Wapp"
        },
        {
          "id": "",
          "title": "Index",
          "collection": {
            "label": "pages",
            "name": "Posts"
          },
          "categories": "",
          "tags": "",
          "url": "/",
          "content": "Hi, \n  I am Michael\n  Software Developer\n  Working fully remote.\nTurning complex business challenges into elegant, maintainable software. Deep technical expertise meets a clear understanding of what moves the needle.\nLearn more about myself, \n  the things that I like, \n  the projects that I'm working on or \n  the articles that I write.\n\n  For collaboration or questions, contact me here"
        },
        {
          "id": "posts",
          "title": "Blog",
          "collection": {
            "label": "pages",
            "name": "Posts"
          },
          "categories": "",
          "tags": "",
          "url": "/posts/",
          "content": "Blog\n  \n    \n      \n      \n    \n  \n\n\n\n  \n\n  \n  \n\n\n\n\n\n    \n      How To: Ruby on Rails, git worktrees and Claude Code\n      \n        February 15, 2026\n          \n              Ruby on Rails\n              git worktrees\n              Claude Code\n          \n      \n    \n    \n      The most efficient way to use your MacBook\n      \n        February 14, 2026\n          \n              Tiling Window Manager\n              Linux\n              macOS\n          \n      \n    \n    \n      Implementing Dynamic Search with Turbo Frames in Ruby on Rails 7\n      \n        January 27, 2024\n          \n              Ruby\n              Rails\n              Bootstrap\n          \n      \n    \n    \n      Integrating Bootstrap 5 Modals with Ruby on Rails 7 Using Turbo Frames\n      \n        January 25, 2024\n          \n              Ruby\n              Rails\n              Bootstrap\n          \n      \n    \n    \n      Setup Dokku (a free Heroku alternative) and Deploy a Ruby on Rails 7 Application\n      \n        August 20, 2023\n          \n              Platform as a\n              Rails\n              Bootstrap"
        },
        {
          "id": "projects",
          "title": "Projects",
          "collection": {
            "label": "pages",
            "name": "Posts"
          },
          "categories": "",
          "tags": "",
          "url": "/projects/",
          "content": "A selection of projects I’ve worked on as a software developer.\n\n\n\nCurrent Projects\n\nBautask\nTechnology Lead | Sept 2025 - Present\n\nConstruction company management software built in Ruby on Rails. Leading the technical direction and architecture of the platform.\n\nTech: Ruby on Rails, Hotwire, Turbo, Stimulus, Tailwind\n\n\n\nBigMaxGolf\nSolo Developer | June 2025 - Present\n\nHelped creating internal tools and process automations to improve their business operations.\n\nTech: next.js, React, Tailwind, Supabase\n\n\n\nAstroDNA BaZi Calculator\nSolo Developer | July 2024 - Present\n\nA Swiss Chinese astrology software to calculate and interpret BaZi (Four Pillars of Destiny). Helped building and extending the application for new features and AI functionality\n\nTech: Ruby on Rails, Tailwind, Hotwire, Stimulus, Wordpress, PHP\n\n\n\nstaqq\nSoftware Developer | June 2024 - Present\n\nJob finding platform for medical professionals in Austria. Contributing to both the web platform and mobile application development.\n\nTech: WordPress, PHP, Slim API, .NET MAUI\n\n\n\nPast Projects\n\nAVIMBU\n(Ex) Cofounder &amp; CTO | July 2023 - December 2025\n\nStartup cofounder (together with Christoph Hafner) building multiple product ideas including a wealth management platform, a personal website builder, an uptime tracker and a innovative debugging solution called HALEA.\n\nTech: Ruby on Rails, Hotwire, Turbo, Stimulus, Tailwind, next.js, React, Python, langchain, llamaindex\n\n\n\nSkynamic\nAutomation Developer | Sept 2025 - Dec 2025\n\nBuilt Slack automation for internal processes of a drone filmmaking company, streamlining their workflow and communication.\n\nTech: Slack API, Automation\n\n\n\nLovebiirds\nSoftware Developer | Jan 2023 - June 2023\n\nWedding planner and wedding service provider marketing platform. Worked on both frontend and backend development.\n\nTech: Angular, Laravel\n\n\n\nNeoom\nSoftware Engineer | July 2021 - Dec 2023\n\nContributed to release and test automation, and Rails application development for this renewable energy company.\n\nTech: Ruby on Rails, Vue.js, React, React Native, Test Automation\n\n\n\nTechnical University of Vienna\nTeaching Assistant | Sept 2014 - June 2021\n\nWorked as a tutor and teaching assistant at the Technical University of Vienna in the fields of databases, structured data, software quality and mobile engineering\n\nTech: Software Quality, Mobile Engineering\n\n\n\nRise World - EPA\nSoftware Engineer | Sept 2019 - June 2021\n\nHelped build the EPA (Elektronische Patientenakte / Electronic Patient Record) mobile application as part of the software engineering team.\n\nTech: Mobile Development in Android and iOS\n\n\n\nBosch Engineering Vienna\nSoftware Intern | March 2016 - Feb 2019\n\nWorked on internal tools development during my internship at Bosch Engineering’s Vienna location.\n\nTech: Internal Tools in JAVA"
        },
        {
          "id": "things",
          "title": "Things I like",
          "collection": {
            "label": "pages",
            "name": "Posts"
          },
          "categories": "",
          "tags": "",
          "url": "/things/",
          "content": "This page is a summary of things I like. It’s a list of technology pieces I like to use. It also contains links to software I like to use. It’s can also be a list of articles or references I like to have on my personal website.\n\nHardware\n\nPhysical things I really appreciate\n\n\n  My MacBook Pro 16 with M2 Pro and 32GB\n  My Kefine Quatio IEM headphones which I use as a high quality audio listening device\n  My Apple Watch Ultra\n  The best charger from Anker\n  An extremely capable retro gaming handheld - the Retroid Pocket 5\n\n\nSoftware\n\nI’m currently an all things Apple fanboy. My journey is rather classic for a software nerd: Coming from Windows, venturing into MacOS (for university - because it was cool) and switching to a full on Arch Linux build with the magnificent i3 Window Manager on a proper ThinkPad. I loved my Linux area but I switched back to MacOS for the easy of use on a daily basis and because I wanted to be able to develop for the Apple ecosystem. I’ve missed the keyboard based navigation easiness of i3 and I found AeroSpace which tremendously changed how I use my Mac nowadays. It’s the missing tiling window manager for MacOS and I love it. Together with Raycast it’s my cheat code to be extremely efficient on MacOS. I wrote more about my journey with tiling window managers in this article."
        },
        {
          "id": "",
          "title": "Michael Bauer-Wapp",
          "collection": {
            "label": "data",
            "name": "Posts"
          },
          "categories": "",
          "tags": "",
          "url": "",
          "content": ""
        }
]
