AI 资讯
24 Days of Coding: An 86-Hour Roadmap from Truck Driver to Web Engineer
1. Introduction Hello from Japan! 🇯🇵 I am an active truck driver in Japan self-studying Python and Web technologies, leveraging my logistics domain knowledge to transition into a Web Engineer. (English is my second language, but I'm excited to share my progress with developers worldwide!) I started my learning journey on May 12, 2026. As of June 4, I have logged 24 days and 86 cumulative hours of study. This article documents my progress, learning roadmap, and project milestones chronologically. Check out my GitHub here👈️ (Note: Most repository documentation and commit messages are currently in Japanese.) 2. The 86-Hour Learning Roadmap A quick breakdown of my 86 hours: 80 hours were dedicated to hands-on development and implementation, and 6 hours were spent on environment configuration, documentation, and workflow optimization. Stages 1–3: Automation & Scraping Projects ① Fundamentals & Web Scraping (25 hours) Learned core Python syntax and built automated scripts to collect targeted web data. ② Puoppo Auto-Analysis System (15 hours) Implemented automated poll data retrieval and AI-powered text analysis on Lubuntu. ③ Bakery Sales Aggregation System (12 hours) Integrated web scraping with automated Excel processing to streamline sales data aggregation. Stage 4: Practical Ruby on Rails ④ rails_practice (23 hours) Explored MVC architecture in Ruby on Rails. Practiced configuration management and Git rebasing to build a solid foundation in web framework operations. Stage 5: Real-World System Integration ⑤ hiroshima-logistics-hub (5 hours) Built a web system to aggregate real-time weather and traffic conditions in the Hiroshima area. Technical Fix : To bypass build crashes on Render (Free Tier / 512MB RAM), I precompiled assets locally and configured SQLite3 database storage in writable directories before deployment. 3. Key Takeaways for Resource-Constrained Development Through these projects, I established three operational principles for developing efficiently within
AI 资讯
Deploying Rails 8 on Render Free Tier: Bypassing the 512MB RAM and Read-Only Storage Limits
1. Introduction Hello from Japan! 🇯🇵 I am an active truck driver in Japan self-studying Python, leveraging my logistics domain knowledge to become a Web Engineer. (English is my second language, but I'm excited to share my journey with developers around the world!) I started my self-study journey on May 12, 2026. In this article, I summarize the process of deploying Ruby on Rails 8 to a PaaS (Render Free Tier) and how I tackled the strict resource constraints I ran into. Check out my GitHub here👈️ (Note: Most repository documentation and commits are currently in Japanese.) 2. Environment Development : Lenovo G580 (Lubuntu 24.04 LTS / 16GB RAM / Upgraded SSD) Production : Render (Free Tier: 512MB RAM) Testing Device : Xiaomi 15T 3. Challenges & Solutions ① Git Repository Structure Inconsistency Issue : An unnecessary .git directory existed inside a subdirectory, causing errors during deployment. Solution : Deleted the nested .git directory to restore repository hierarchy integrity. ② Build Failure via Render Free Tier RAM Limit (512MB) Issue : Executing asset compilation on Render triggered Out-Of-Memory (OOM) crashes, forcibly killing the build process. Solution : Precompiled assets locally and committed the static files to the repository, significantly reducing memory usage on the production build server. ③ SQLite3 Write Permission Error Issue : Encountered database write permission errors during CRUD operations in production. Render's file system is read-only by default, except for designated directories (such as storage/ ). Solution : Updated config/database.yml to direct the SQLite3 database file to a path with write permissions (e.g., under storage/ ). 4. Conclusion By applying these workarounds, I successfully verified the deployment and operation of a Rails 8 application on Render's Free Tier. (Please note: Although production runtime works properly, because the setup prioritizes local configurations, some automated CI tests on GitHub currently report errors.
AI 资讯
Production-Ready AI Agents: How to Deploy Without Losing Your Database
I watched an AI agent send 200 emails to the wrong recipients because I forgot one validation check. The emails were well written. The offers were real. The recipients were just... not our leads. That was early. I learned fast. Every agent I build now has three layers of guardrails before it touches a database or an API. Here's exactly what those layers look like and why they're non-negotiable for production. Input Validation: Your Prompt Is Not a Schema The first mistake people make is trusting the LLM to produce valid output. It won't. Not reliably. I've seen GPT-4 return a JSON key called "emial" instead of "email" in a critical pipeline. One typo, and the whole record is garbage. The fix is a strict validation layer that runs before any data reaches your system. In my AI resume tailor, I use a JSON schema with conditional presence flags. Every field that must be real has a has_* boolean guard. If the LLM tries to fabricate a phone number, the schema rejects it. const resumeSchema = z . object ({ contact : z . object ({ email : z . string (). email (), phone : z . string (). optional (), has_phone : z . boolean () }). refine ( data => { // If phone is present, the guard must be true return data . phone ? data . has_phone : ! data . has_phone }, " Phone number present but has_phone flag is false " ) }) This pattern catches hallucinations before they corrupt your database. The schema is the contract. The LLM is just a suggestion engine. Permission Scoping: Give Agents the Minimum They Need An agent should never have write access to tables it doesn't need. That sounds obvious, but I've seen production systems where a job description rewriting agent had full CRUD access to the user table. When I built the LLM scoring pipeline for a job board platform, I created separate database roles. The scoring agent only had SELECT on the job listings table and INSERT on a scoring results table. It never touched users, applications, or configuration. Even if the prompt was hijack
AI 资讯
Add newsletter subscriptions to Rails 8 signups
Users are creating accounts on your new Saas. Yay (and not just family and friends or bots). Yay! Now comes the next step from every marketing handbook: capturing newsletter subscriptions. This article builds on Add Sign Up to Rails 8’ Authentication . Add a simple checkbox to let users opt in to product updates during signup. Store their preference using Rails Vault and manage the subscription with Rails Courrier . First, add Rails Vault and Rails Courrier to your Gemfile: gem "rails_vault" gem "rails_courrier" Rails Vault adds simple and easy settings, preferences and so on to any ActiveRecord model (I recently pushed 1.0.0). Courrier is API-powered email delivery for Ruby apps with support for Mailgun, Postmark, Resend and more. Rails Courrier is the Rails “wrapper” for Courrier. These two gems work really nicely together for this feature. Run bundle install and generate the Rails Vault migration: rails generate rails_vault:install rails db:migrate It creates a new file app/models/user/subscriptions.rb : class User::Subscriptions < Vault vault_attribute :product_emails_subscribed_at , :datetime # Add more subscription types as needed: # vault_attribute :marketing_emails_subscribed_at, :datetime # vault_attribute :weekly_digest_subscribed_at, :datetime end And updates your User model to use this vault: # app/models/user.rb class User < ApplicationRecord + vault :subscriptions has_secure_password has_many :sessions , dependent: :destroy end This keeps subscription data organized without cluttering your User table. More subscription types can be added later without database migrations. Now the plumbing is done, add a checkbox to your signup form in app/views/signups/new.html.erb : <%= form . check_box :product_emails %> <%= form . label :product_emails , "Subscribe to product updates" %> Update the Signup model to accept this parameter: # app/models/signup.rb class Signup include ActiveModel :: Model include ActiveModel :: Attributes attribute :email_address , :stri
AI 资讯
LLM Evaluation System Prompts Scored Rubrics Runtime Guardrails: A Practical Guide for Production
LLM Evaluation System Prompts Scored Rubrics Runtime Guardrails: A Practical Guide for Production Learn how to evaluate LLM outputs in production using system prompts, scored rubrics, and runtime guardrails to prevent hallucinations and ensure quality. TL;DR: To evaluate LLM outputs in production, combine system prompts that define evaluation criteria, scored rubrics using LLM-as-a-judge for dimensions like correctness and relevance, and runtime guardrails that filter or flag unsafe outputs. This approach scales better than human review, adapts via prompt changes, and catches failures that status codes miss, as seen in the Air Canada chatbot case. Why Production LLM Evaluation Demands More Than Status Codes A 200 status code only confirms the server processed the request—it says nothing about whether the generated text is factual, safe, or useful. The Air Canada chatbot that invented a non-existent bereavement discount returned perfectly valid HTTP responses, yet the hallucinated policy led to a tribunal ruling against the airline. Production evaluation must therefore separate operational health (latency, error rates) from output quality (correctness, relevance, harmlessness). Consider a typical API call that succeeds operationally but fails qualitatively: import requests response = requests . post ( " https://api.example.com/v1/chat " , json = { " model " : " gpt-4o " , " messages " : [{ " role " : " user " , " content " : " What is Air Canada ' s bereavement policy? " }]}, headers = { " Authorization " : " Bearer $KEY " } ) print ( response . status_code ) # 200 print ( response . json ()[ " choices " ][ 0 ][ " message " ][ " content " ]) # Output: "Air Canada offers full refunds for bereavement-related cancellations..." A 200 status code and a well-formed JSON body mask a completely fabricated policy. To catch this, you need a separate evaluation layer that scores the output against a rubric. LLM-as-a-judge is a common approach, using a second model to assess the
安全
Now, defenders are embracing the prompt injection, too
"Context bombing" tricks hacking agents into shutting down before they can do harm.
AI 资讯
Your next model upgrade won't close this gap
There's a comfortable thing people say when they see an AI agent query a code map. "Nice crutch. For now." The logic underneath it is reasonable. Coding agents are young. Context windows are small and getting bigger. Models are dumb today and will be smart tomorrow. So a structural index, the thing that hands the agent a dependency graph it would otherwise have to reconstruct, looks like a patch over a temporary weakness. Wait two releases. The model will just hold the whole repo in its head and the map becomes a quaint workaround, like a spellchecker for someone who learned to spell. I build one of those maps Sense . I went looking for the data that would kill it. I didn't find it. I found the opposite. What a map hands an agent is a computed fact. What a better model hands you is a more confident guess . No amount of model progress turns the second into the first, because the difference between them isn't a quality gap that closes with scale. It's a difference of kind. The rest of this piece is the two findings that forced me there. The belief, stated fairly The claim at full strength, because a weak version is easy to knock over. A code map exists to compensate for what the model can't do yet. Today's agent greps, samples, and guesses at structure because it can't read the whole codebase at once. Tomorrow's agent reads all of it, reasons over all of it, and the guessing stops. Bigger windows plus better weights equal no more blind spots. The map is scaffolding you'll tear down once the building stands. If that's true, the right move is to skip the tool and wait. Both findings, in order. Proof one: the best model available was still blind The benchmark ran the same task on thirteen real Ruby repos. Pick the hub model of an app, the Inbox , the MergeRequest , the Spree::Order , and ask the agent to find every place that depends on it before a teardown change. The non-obvious dependents, the ones scattered through concerns and workers and config-string registries, w
AI 资讯
"Ruby is the most AI-friendly stack" is half true
You've seen the claim in every Ruby thread for the past year. Ruby and Rails are the most AI-friendly stack. Fewer tokens, less hallucination, the model just writes it cleanly. Half of that claim I'll concede without a fight . The other half I measured, across thirteen real Ruby codebases, and that's where a line shows up, sharp enough to put every repo on one side or the other. Including yours. The half that's true: writing Ruby is solved Start with the part that holds up, because it really does. A model that has seen ten thousand Rails apps knows where the model lives, where the job goes, what a concern does, what has_many implies, before it reads a line of yours. Convention over configuration was always written partly for the next human reading the code. It turns out the model is the next reader too, and the conventions answer half its questions before it asks them. So "write me a service object," "add a scope," "refactor this controller"? The stack carries the model. Fewer wrong guesses, tighter loops, less to hallucinate because the shape is already known. Anyone who builds on Rails has lived this, and the AI-friendly reputation earned it. I'm not here to take that away. I'm here to point out it answers a question nobody dangerous is asking. The half that isn't: navigating Ruby at scale "Can AI write Ruby" is settled. The question that ships broken deploys is different: can AI navigate Ruby? What breaks if this model changes, who depends on it, where the blast radius ends. Reading and navigating feel like the same skill when you're fluent. They are not the same skill for an agent. Reading a file is local, the answer is right there in the text. Navigating is structural, the answer lives in the edges between files, what calls what, what breaks what, and no single file contains it. So I ran the structural question on all thirteen repos. Same task each time: take the hub model, the Inbox , the MergeRequest , the Spree::Order , and find every dependent before a tear
AI 资讯
Choosing the Right Backend Framework: Django vs. Gin vs. Ruby on Rails.
Every application we use today—from banking apps to social media platforms—has something working behind the scenes. That hidden engine is called the backend. The backend is responsible for processing requests, storing data, handling authentication, enforcing business rules, and ensuring everything works as expected when users interact with an application. One of the first decisions backend developers make is choosing a framework. A framework provides the tools, structure, and best practices needed to build applications faster and more securely. Today, let's look at three popular backend frameworks: Django, Gin, and Ruby on Rails. Django (Python) Django is one of the most mature and feature-rich backend frameworks available. Built using Python, it follows the philosophy of "batteries included." This means many features developers need are already built into the framework, including: User authentication Admin dashboard Database ORM Security protections URL routing Form validation Because so much comes ready to use, developers can spend more time solving business problems instead of rebuilding common features. Best for: Content management systems E-learning platforms Business applications APIs Startups building products quickly Advantages: Fast development Excellent security features Large community Extensive documentation Scales well for many applications Trade-offs: The framework includes many components, so it can feel heavier than minimalist frameworks. Gin (Go) Gin is a lightweight web framework built for the Go programming language. Unlike Django, Gin keeps things minimal. It gives developers speed and flexibility while letting them choose many of the additional tools they want to use. One reason many developers enjoy Gin is its impressive performance. Since Go is a compiled language designed for concurrency, Gin can efficiently handle many requests simultaneously while using relatively few system resources. Best for: REST APIs Microservices High-performance syst
AI 资讯
Pagination: Always a "sort" (of) mistake [bugfix]
Pagination is a key component on web-applications that let users navigate through pages making easy to read/find records. Also, pagination is a great strategy to improve performance by avoiding to load entire dataset at once. However, while working with Kaminari, a popular pagination gem in Rails, I encountered an unexpected issue that revealed an interesting edge case. Identify the issue Basically, pagination in frontend was not working properly. Datatable should load 316 total rows, although when the user started to load 15 records per page, frontend is showing inaccurate total rows. Multiple of 15 should ends at 0 or 5. There were pages with 64 records, crazy world. Some pages were loading 12 or 11 rows. There is no issues or error messages in the frontend or backend. Lost in debugging-land After discarding Angular frontend errors, I started to dig into backend controller and Kaminari configuration. Nothing seems wrong. Everything looked good: test suite, smoke tests, desktop debugging. Despite of test results, I started to wonder: what if returned-data is wrong after all? and... Bingo! Finally, after checking every response I noticed that there were duped records in two different pages(pagination requests). Those duped records were skipped from Angular data-table and that's why loaded/total rows did not match. Bingo: a sort of mistake This tricky bug has a simple explanation: bad sorting. Kaminari uses a SQL query using LIMIT/OFFSET strategy: SELECT * FROM posts ORDER BY id LIMIT 25 OFFSET 0 ORDER BY : sort the collection. LIMIT : number of records per page. OFFSET : is used to skip a specified number of rows before starting to return rows from a query. This works perfectly using ORDER BY id because primary key is unique. Check table A. id title body created_at lock 101 Welcome to the Platform First post introducing the new platform features. 2026-06-16 08:15:22 false 102 Summer Update Announcing the latest improvements and updates. 2026-06-16 08:15:22 true 103
AI 资讯
Pro File Uploads in Rails 8: Speed and Scalability with Direct Uploads
Imagine a user trying to upload a 100MB video or a high-resolution photo to your app. If you use the standard Rails file upload, that file travels from the user's browser to your Rails server, and then your server sends it to S3 or Google Cloud. This is a terrible way to do it. While that 100MB file is transferring, your Rails worker (Puma) is frozen. It can't handle other users. If three people upload large files at once, your whole app will stop responding. In 2026, the professional way to handle this is Direct Uploads . With Direct Uploads, the file goes directly from the user's browser to your cloud storage (S3, R2, etc.). Your Rails server only handles a tiny bit of metadata. It is faster for the user and much safer for your server. Here is how to set it up in Rails 8. STEP 1: Configure Your Storage First, make sure you aren't using the local disk for production. You need a cloud provider like AWS S3 or Cloudflare R2. In your config/storage.yml : amazon : service : S3 access_key_id : <%= ENV['AWS_ACCESS_KEY_ID'] %> secret_access_key : <%= ENV['AWS_SECRET_ACCESS_KEY'] %> region : us-east-1 bucket : my-app-uploads # Crucial for Direct Uploads! public : true Note: You must configure CORS in your S3/R2 dashboard to allow requests from your domain. If you don't do this, the browser will block the upload. STEP 2: The Rails Form Rails makes the backend part incredibly easy. You just add one attribute to your file field: direct_upload: true . <!-- app/views/users/_form.html.erb --> <%= form_with ( model: user ) do | f | %> <div class= "field" > <%= f . label :avatar %> <%= f . file_field :avatar , direct_upload: true %> </div> <%= f . submit "Save Profile" %> <% end %> When you add direct_upload: true , Rails automatically includes a JavaScript library that handles the "handshake" with S3. STEP 3: Adding a Progress Bar (The UX Win) Direct uploads can take a few seconds. If nothing happens on the screen, the user will think your app is broken. We can use the built-in Ac
AI 资讯
A tech worker-backed PAC is bringing a $5M knife to Big Tech’s $100M gunfight
Guardrails positions itself as a populist political movement that runs on small donations from people in the trenches of the AI boom.
AI 资讯
Rails GuardDog: Advanced Security Scanner for Rails Applications
Rails GuardDog: Advanced Security Scanner for Rails Introduction Today I'm excited to announce Rails GuardDog v0.1.0 — an open-source security scanner for Rails that goes beyond traditional tools like Brakeman. While Brakeman is excellent for catching basic Rails vulnerabilities, Rails GuardDog focuses on newer vulnerability classes that most tools miss: AI/LLM prompt injection, DoS/ReDoS patterns, supply chain attacks, and more. The Problem Modern Rails applications face new security challenges: AI/LLM Integration - How do you prevent prompt injection when integrating with ChatGPT, Claude, or Anthropic? ReDoS Attacks - Catastrophic backtracking in regex can bring down your app Supply Chain Attacks - Typosquatted gems that look like popular libraries IDOR Gaps - Objects accessible without proper authorization checks Advanced Secrets - Hardcoded API keys that Brakeman misses Rails GuardDog detects all of these. What is Rails GuardDog? Rails GuardDog is a lightweight gem that adds comprehensive security scanning directly to your Rails applications. 12 Security Checkers SQL Injection - String interpolation in queries XSS - Unescaped output in views CSRF - Disabled protection verification Mass Assignment - permit! vulnerabilities (fixes Brakeman #1942, #1918) Open Redirect - User input in redirects Hardcoded Secrets - API keys, tokens, passwords (always-on, fixes #1989) DoS/ReDoS - Unbounded queries, dangerous regex patterns IDOR - Object access without authorization AI/LLM Prompt Injection - User input flowing to LLMs Rate Limiting - Missing rack-attack configuration Supply Chain - Typosquatted gems using Levenshtein distance GraphQL - Missing field-level authorization Features 📊 Multiple report formats : Console, HTML, JSON 🔍 AST-based analysis : Uses parser gem for deep code understanding ⚡ Async support : Built-in Sidekiq integration 📈 Zero dependencies : Only requires parser and ast gems 🚀 Production-ready : Tested and battle-ready 📝 CWE/OWASP mappings : Every find
AI 资讯
I’m Blown Away by Kamal
My Previous Deployment Choices Since I mostly do web development using Ruby on Rails, these were my go-to options for deployment: PaaS like Heroku, Render, or Railway Serverless setups (Cloud Run + NeonDB) Honestly, I didn’t have any major complaints. PaaS costs a bit more, but in return, you get a clean UI and dead-simple workflows like GitHub integration. If the cost bothered me, I’d just go serverless. For my personal servers—where huge traffic isn't exactly a concern—going serverless meant the app would just sleep when inactive, allowing me to run services for around 50 yen a month. Compared to the headache of clicking through complex AWS or GCP dashboards to piece things together based on architecture diagrams, it was a walk in the park. I was perfectly content. Seriously. Kamal Became the Default in Rails 8 Everything changed when Rails 8 dropped. I heard they adopted Kamal as the official deployment tool. Kamal — Deploy web apps anywhere From bare metal to cloud VMs using Docker, deploy web apps anywhere with zero downtime. kamal-deploy.org Kamal? Is deploying really going to get any easier? I mean, I’m doing completely fine right now, though... That’s what I thought. But once I gave it a shot, it felt like being struck by lightning. This is an absolute game-changer. All you have to do is run rails new , throw your server's IP address into deploy.yml , and run kamal setup . That’s it—your app is deployed. For every release after that, it's just kamal deploy . I couldn't believe how simple the deployment workflow was. # deploy.yml service : my-app image : my-user/my-app servers : web : - 192.0.2.1 # Just swap in your VPS IP address here proxy : ssl : true host : app.example.com # Set up your domain here Sure, you have to bring your own server, but Kamal prides itself on being able to deploy absolutely anywhere. I rented a couple of VPS instances from Hetzner for about $10 a month each to host SuperRails and LazyCafe . From what I looked into, you can't really
AI 资讯
🇺🇸 3 Essential Gems to Eliminate Friction in Your Rails Workflow
Anyone who works with Ruby on Rails knows that, despite the framework being incredible for productivity, there are some classic workflow deficiencies that haunt almost every project. You are focused on writing code, but suddenly you need to open an external tool like Postman to test a route. Then, you run a complex script to generate a static database diagram. And at the end of the day, you still need to manually update the API documentation, which will inevitably become outdated in the next sprint. This constant context switching and manual maintenance generates enormous friction. To cover these deficiencies, I developed three gems that bring these tools inside your application. They are so practical that they quickly become indispensable in any Rails project. Meet each one of them: 1. rails-api-docs : The End of Outdated Documentation The deficiency: API documentation always starts with good intentions, but as the system evolves—new routes, parameters, and response fields—it quickly stops representing reality. Keeping this updated manually is repetitive and frustrating work. The solution: The rails-api-docs gem solves this by leveraging what Rails already knows. It inspects your routes, controllers (via AST analysis using Prism), and the ActiveRecord schema to automatically generate the first draft of your documentation. Everything is saved in a single YAML file ( config/rails-api-docs.yml ), which serves as the single source of truth. Why it is indispensable: Append-only strategy: When adding new routes and running the generator, the gem only appends what's new. Your descriptions, custom examples, and tags are never modified or deleted, making the documentation a living document. Zero development friction: You edit the YAML in one window and view the updated documentation in the browser at localhost:3000/rails/api-docs instantly, with no build step required. For production, it exports a single static HTML file without any external dependencies. 2. rails-http-lab
AI 资讯
Presentation Slides for RubyConf Austria 2026 Talk "Frontend Ruby on Rails with Glimmer DSL for Web"
My talk “Frontend Ruby on Rails with Glimmer DSL for Web” went well at RubyConf Austria 2026 . Especially given that after the talk, Chad Fowler (the starter of RubyConf and famous book author of The Passionate Programmer , among other books) told me “good job”, and Obie Fernandez (a famous entrepreneur and book author of The Rails Way , among other books) told me he will try Glimmer DSL for Web because he doesn’t like React.js. Presentation Slides Direct Original Long Link: https://docs.google.com/presentation/d/e/2PACX-1vQ9oBnZpzK_eicVLGSqDmVzhsXsblONEKepnw5_xGHGXTM52JSjaS_ObYUJbx-zkb1M2ul9N2A2MnvU/pub?start=false&loop=false&delayms=60000&slide=id.g140fe579a5a_0_0 Glimmer DSL for Web GitHub: https://github.com/AndyObtiva/glimmer-dsl-web I ran a poll at the beginning of my talk, and everyone agreed that they love Ruby and that Ruby is superior to JavaScript, plus the majority indicated that they’d like to write less JavaScript and more Ruby during their Rails web development work. Several attendees told me my talk was great after the talk. Charles Nutter had me help him with his JRuby workshop afterwards by showcasing my other Glimmer project, Glimmer DSL for SWT , which runs on JRuby. In about 1 minute, I scaffolded a Hello World desktop app from scratch and then packaged it as a native executable on the Mac. Attendees were impressed. So, I’ve participated in presenting 2 events at this conference. I am very grateful for having such a great experience at RubyConf Austria 2026 overall, especially given that it uniquely included several classical/neoclassical/jazz concerts in between talks that entertained us and relaxed us. Chad Fowler concluded the conference with a beautiful Jazz piano and sax performance. Shout out to Hans Schnedlitz, Muhamed Isabegovic, and Zuzanna Kusznir (plus everyone who helped out) for organizing and hosting such a special Ruby conference!!! Original blog post version: https://andymaleh.blogspot.com/2026/06/rubyconf-austria-2026-frontend-r
AI 资讯
I open-sourced a modern acts_as_tenant alternative for Rails 7+
--- title : " Introducing rails-tenantify: Row-Level Multi-Tenancy for Rails 7+" published : true description : " A modern, safe, and robust row-level multi-tenancy gem for Ruby on Rails. Prevent data leaks, protect bulk writes, and preserve tenant context in background jobs." tags : rails, ruby, opensource, saas --- ## The Problem Every multi-tenant SaaS app eventually needs to answer the same questions: * How do we make sure School A never sees School B's data? * How do we scope every query to the right organization? * How do we keep tenant context alive in background jobs and Sidekiq retries? * How do we stop a careless `update_all` from wiping another tenant's rows? The typical answer is *"use acts_as_tenant"* or *"switch to Apartment."* But in modern Rails development, that often means: * Fighting unmaintained APIs on Rails 7+ * Losing tenant context when a background job retries * Dealing with schema-per-tenant complexity (Apartment) and heavy DevOps overhead * Rolling your own `default_scope` and crossing your fingers that nobody calls `unscoped` For most Rails apps, you just need **row-level tenancy** : one database, one `organization_id` column, and strict scoping. The pattern is simple. Getting it **safe** in production is not. --- ## What I Built **`rails-tenantify`** is a Ruby gem that adds row-level multi-tenancy directly to your Rails models and controllers. No external services, no extra databases per tenant—just your own PostgreSQL (or SQLite in dev). ruby class Project < ApplicationRecord include Tenantify::Scoped belongs_to_tenant :organization end ### Set the tenant once per request ruby class ApplicationController < ActionController::Base set_tenant_by :subdomain # acme.yourapp.com → Organization end ### Everything scopes automatically ruby Tenantify.current_tenant = current_organization Project.all # Only this org's projects Project.create!(name: "Q2 Roadmap") # organization_id is set automatically ### Switch context safely for admins or scripts