- 后端
【免费下载链接】koudoku
Robust subscription support for Rails with Stripe.
Koudoku 是一款为 Ruby on Rails 应用提供 Stripe 订阅支持的开源引擎,内置定价页、支付页和订阅管理页面。它最大的亮点是"一条命令装好":运行rails g koudoku:install user后,install 生成器会自动创建 3 个模型、向现有模型注入关联、并把引擎挂载到路由里。这篇文章带你拆解 install 与 views 两个生成器的源码,看看这套"自动注入"是如何实现的 🧩
1️⃣ 先认清两个生成器的分工
Koudoku 提供了两个 Rails 生成器,职责非常清晰:
| 生成器 | 命令 | 作用 |
|---|---|---|
| install | rails g koudoku:install user | 一键安装:生成 3 个模型、注入关联与路由、输出初始化配置 |
| views | rails g koudoku:views | 把引擎内置的订阅页面整套复制到宿主应用,方便二次定制 |
核心源码位置:
- install 生成器:lib/generators/koudoku/install_generator.rb
- views 生成器:lib/generators/koudoku/views_generator.rb
- 模板目录:lib/generators/koudoku/templates/
两个生成器在 lib/koudoku.rb 中被显式注册:
require "generators/koudoku/install_generator" require "generators/koudoku/views_generator"此外,lib/tasks/koudoku_tasks.rake 还封装了rake koudoku:install和rake koudoku:views两个快捷任务,本质只是转调上面的 rails 命令。
2️⃣ install 生成器:四步完成注入"手术"
声明必需参数:订阅的"主人"模型
argument :subscription_owner_model, :type => :string, :required => true, :desc => "Owner of the subscription"命令后必须跟一个参数(通常是user,也可能是company),表示"谁拥有订阅"。紧接着有一个小技巧——重写argument自动生成的访问器,保证该参数在模板插值中永远以小写形式出现:
def subscription_owner_model @subscription_owner_model.downcase end三连招:一次生成 3 个模型
对每个模型,Koudoku 都采用generate+template两步组合:先调用 Rails 内置 model 生成器建表(会自动生成 migration),再用内置模板覆盖模型文件,注入业务逻辑:
# 生成 subscription(字段名过长,此处省略号略) generate("model", "subscription stripe_id:string plan_id:integer ... #{subscription_owner_model}_id:integer") template "app/models/subscription.rb" # 生成 plan 与 coupon,套路相同三个模板文件分别是:
- templates/app/models/subscription.rb:include 核心 concern Koudoku::Subscription,并声明 owner 关联
- templates/app/models/plan.rb:套餐模型,
has_many :subscriptions - templates/app/models/coupon.rb:优惠码模型
模板是标准的 ERB 写法,比如 subscription 模板里的:
belongs_to :<%= subscription_owner_model %>生成时传入user,落盘后就是belongs_to :user。一份模板适配任意 owner 模型,正是生成器机制的精髓 ✨
同时,source_paths告诉 Thor 到两个地方找模板:引擎根目录和生成器旁边的 templates 目录:
def self.source_paths [Koudoku::Engine.root, File.expand_path("../templates", __FILE__)] end关联注入:不动一行现有代码
owner 模型(通常是User)是宿主应用已有的模型,Koudoku 不能覆盖它,于是用 Thor 的inject_into_class精确插入一行关联:
inject_into_class "app/models/#{subscription_owner_model}.rb", subscription_owner_model.camelize.constantize, "# Added by Koudoku.\n has_one :subscription\n\n"三个参数分别是:目标文件、目标类名(user动态驼峰化为User)、要插入的代码。执行后has_one :subscription被干净利落地写进app/models/user.rb的类体内部,其余代码分毫未动 🔪
路由注入:挂载引擎 + 暴露定价页
最后一步用route动作向config/routes.rb末尾追加内容:
route <<-RUBY # Added by Koudoku. mount Koudoku::Engine, at: 'koudoku' scope module: 'koudoku' do get 'pricing' => 'subscriptions#index', as: 'pricing' end RUBYmount挂载的 Koudoku::Engine 使用isolate_namespace Koudoku做命名空间隔离,引擎内部的路由定义在 config/routes.rb:订阅资源、取消订阅的 member 路由,以及把 StripeEvent 引擎挂在/webhooks上接收支付回调(默认订阅的事件见 config/initializers/stripe_event.rb)。而外层的pricing路由则让宿主应用可以直接用pricing_path链接到定价页。
生成器还会顺手用template生成 templates/config/initializers/koudoku.rb,其中subscriptions_owned_by被动态填充为 owner 模型,Stripe API key 默认读取STRIPE_PUBLISHABLE_KEY/STRIPE_SECRET_KEY环境变量——密钥不进版本库。最后终端会提示你把 Stripe webhook 指向/koudoku/events,安装流程至此闭环。
3️⃣ views 生成器:极简的"全量复制"方案
views 生成器只有十几行核心逻辑:把引擎里app/views/koudoku/subscriptions/目录下的所有文件复制到宿主应用同名路径:
files_to_copy = Dir.entries("#{Koudoku::Engine.root}/app/views/koudoku/subscriptions") - %w[. ..] files_to_copy.each do |file| copy_file file, "app/views/koudoku/subscriptions/#{file}" end被复制的源文件就在 app/views/koudoku/subscriptions/,包括index(定价页)、new/edit/show(订阅流程)、_pricing_table、_stripe_js、unauthorized等默认视图。
为什么单独做这个生成器?因为默认安装时只会复制一个_social_proof.html.erb(用户评价占位片段,由 install 生成器 copy 进去)。Rails 引擎的视图查找顺序是"宿主应用优先于引擎",所以想改某一张页面时手动复制那一个文件即可;想全套接管时,rails g koudoku:views一条命令全部落盘 📦
4️⃣ 总结:三个值得借鉴的设计点
- generate + template 组合拳:表结构交给 Rails 官方 model 生成器(migration 自动生成),业务逻辑交给 ERB 模板,一个模板适配任意 owner 模型。
- 外科手术式注入:用
inject_into_class和route往现有文件里精准插代码,而不是覆盖整个文件,天然避免与宿主代码冲突。 - 引擎隔离:Koudoku 作为独立 Rails::Engine 运行,视图、路由、资产全部收敛在
koudoku命名空间下,宿主应用只需mount一下即完成集成。
📋 附:生成物速查表
| 生成/修改的文件 | 来源动作 |
|---|---|
app/models/subscription.rb、plan.rb、coupon.rb | generate+template |
| 对应 3 份 migration | generate("model", ...)自动产出 |
config/initializers/koudoku.rb | template |
app/models/user.rb(插入has_one) | inject_into_class |
config/routes.rb(追加 mount 与 pricing 路由) | route |
app/views/koudoku/subscriptions/_social_proof.html.erb | copy_file |
| 整套订阅视图(可选) | views 生成器 |
回过头看整个安装体验:你传入的user参数一路贯穿了模型字段(user_id)、模板插值、关联注入和初始化配置。这就是 Rails 生成器机制优雅的地方——把"约定"固化进生成器,把"选择"留给开发者 ⚡
- 后端
【免费下载链接】koudoku
Robust subscription support for Rails with Stripe.
相关推荐
hygen 实战:为 Express 应用自动生成路由与 Handler 的注入模板指南
hygen 实战:为 Express 应用自动生成路由与 Handler 的注入模板指南 Express.js 被广泛认为是 Node.js 生态中最流行的 W
开发工具CLI终极指南:深度解析MessagePack-CSharp源码生成器如何实现自动化代码生成
终极指南:深度解析MessagePack CSharp源码生成器如何实现自动化代码生成 MessagePack CSharp源码生成器是一个革命性的工具,通过R
序列化后端Soup主动学习采样(active-sample):从海量数据挑出最硬的10%训练
Soup主动学习采样(active sample):从海量数据挑出最硬的10%训练 Soup 是一个"一个 YAML 文件就能微调 LLM"的训练框架,内置的
人工智能大模型微调LoRACLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考