├── examples
├── railsapp
│ ├── log
│ │ └── .keep
│ ├── tmp
│ │ ├── .keep
│ │ ├── pids
│ │ │ └── .keep
│ │ └── storage
│ │ │ └── .keep
│ ├── lib
│ │ └── tasks
│ │ │ └── .keep
│ ├── script
│ │ └── .keep
│ ├── storage
│ │ └── .keep
│ ├── vendor
│ │ ├── .keep
│ │ └── javascript
│ │ │ └── .keep
│ ├── test
│ │ ├── helpers
│ │ │ └── .keep
│ │ ├── mailers
│ │ │ └── .keep
│ │ ├── models
│ │ │ ├── .keep
│ │ │ └── post_test.rb
│ │ ├── system
│ │ │ ├── .keep
│ │ │ └── posts_test.rb
│ │ ├── controllers
│ │ │ ├── .keep
│ │ │ └── posts_controller_test.rb
│ │ ├── fixtures
│ │ │ ├── files
│ │ │ │ └── .keep
│ │ │ └── posts.yml
│ │ ├── integration
│ │ │ └── .keep
│ │ ├── application_system_test_case.rb
│ │ └── test_helper.rb
│ ├── app
│ │ ├── assets
│ │ │ ├── images
│ │ │ │ └── .keep
│ │ │ └── stylesheets
│ │ │ │ └── application.css
│ │ ├── models
│ │ │ ├── concerns
│ │ │ │ └── .keep
│ │ │ ├── post.rb
│ │ │ └── application_record.rb
│ │ ├── controllers
│ │ │ ├── concerns
│ │ │ │ └── .keep
│ │ │ ├── application_controller.rb
│ │ │ └── posts_controller.rb
│ │ ├── views
│ │ │ ├── layouts
│ │ │ │ ├── mailer.text.erb
│ │ │ │ ├── mailer.html.erb
│ │ │ │ └── application.html.erb
│ │ │ ├── posts
│ │ │ │ ├── show.json.jbuilder
│ │ │ │ ├── index.json.jbuilder
│ │ │ │ ├── _post.json.jbuilder
│ │ │ │ ├── new.html.erb
│ │ │ │ ├── edit.html.erb
│ │ │ │ ├── show.html.erb
│ │ │ │ ├── _post.html.erb
│ │ │ │ ├── index.html.erb
│ │ │ │ └── _form.html.erb
│ │ │ └── pwa
│ │ │ │ ├── manifest.json.erb
│ │ │ │ └── service-worker.js
│ │ ├── helpers
│ │ │ ├── posts_helper.rb
│ │ │ └── application_helper.rb
│ │ ├── mailers
│ │ │ └── application_mailer.rb
│ │ ├── javascript
│ │ │ ├── application.js
│ │ │ └── controllers
│ │ │ │ ├── hello_controller.js
│ │ │ │ ├── application.js
│ │ │ │ └── index.js
│ │ └── jobs
│ │ │ └── application_job.rb
│ ├── .ruby-version
│ ├── bin
│ │ ├── dev
│ │ ├── rake
│ │ ├── importmap
│ │ ├── thrust
│ │ ├── jobs
│ │ ├── rails
│ │ ├── brakeman
│ │ ├── rubocop
│ │ ├── docker-entrypoint
│ │ ├── kamal
│ │ ├── setup
│ │ └── bundle
│ ├── .kamal
│ │ ├── hooks
│ │ │ ├── docker-setup.sample
│ │ │ ├── post-proxy-reboot.sample
│ │ │ ├── pre-proxy-reboot.sample
│ │ │ ├── post-deploy.sample
│ │ │ ├── pre-connect.sample
│ │ │ ├── pre-build.sample
│ │ │ └── pre-deploy.sample
│ │ └── secrets
│ ├── public
│ │ ├── icon.png
│ │ ├── robots.txt
│ │ ├── icon.svg
│ │ ├── 404.html
│ │ ├── 400.html
│ │ ├── 406-unsupported-browser.html
│ │ ├── 500.html
│ │ └── 422.html
│ ├── config
│ │ ├── environment.rb
│ │ ├── boot.rb
│ │ ├── importmap.rb
│ │ ├── initializers
│ │ │ ├── assets.rb
│ │ │ ├── filter_parameter_logging.rb
│ │ │ ├── inflections.rb
│ │ │ └── content_security_policy.rb
│ │ ├── recurring.yml
│ │ ├── cache.yml
│ │ ├── queue.yml
│ │ ├── credentials.yml.enc
│ │ ├── cable.yml
│ │ ├── routes.rb
│ │ ├── locales
│ │ │ └── en.yml
│ │ ├── application.rb
│ │ ├── storage.yml
│ │ ├── database.yml
│ │ ├── puma.rb
│ │ ├── environments
│ │ │ ├── test.rb
│ │ │ ├── development.rb
│ │ │ └── production.rb
│ │ └── deploy.yml
│ ├── config.ru
│ ├── Rakefile
│ ├── .rubocop.yml
│ ├── db
│ │ ├── migrate
│ │ │ └── 20241216101156_create_posts.rb
│ │ ├── seeds.rb
│ │ ├── cable_schema.rb
│ │ ├── cache_schema.rb
│ │ ├── schema.rb
│ │ └── queue_schema.rb
│ ├── .github
│ │ ├── dependabot.yml
│ │ └── workflows
│ │ │ └── ci.yml
│ ├── .gitattributes
│ ├── README.md
│ ├── .gitignore
│ ├── .dockerignore
│ ├── Dockerfile
│ ├── Gemfile
│ └── Gemfile.lock
└── memory.rb
├── .envrc
├── .gitignore
├── Gemfile
├── .github
└── cover.png
├── lib
├── libsql_activerecord.rb
└── active_record
│ └── connection_adapters
│ └── libsql_adapter.rb
├── libsql_activerecord.gemspec
├── LICENSE
├── flake.nix
├── Gemfile.lock
├── README.md
├── flake.lock
└── gemset.nix
/examples/railsapp/log/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/tmp/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/lib/tasks/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/script/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/storage/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/tmp/pids/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/vendor/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/test/helpers/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/test/mailers/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/test/models/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/test/system/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/tmp/storage/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/app/assets/images/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/app/models/concerns/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/test/controllers/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/test/fixtures/files/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/test/integration/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/vendor/javascript/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.envrc:
--------------------------------------------------------------------------------
1 | watch_file gemset.nix
2 | use flake
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .direnv
2 | *.db*
3 | .DS_Store
4 |
--------------------------------------------------------------------------------
/examples/railsapp/.ruby-version:
--------------------------------------------------------------------------------
1 | ruby-3.3.6
2 |
--------------------------------------------------------------------------------
/examples/railsapp/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/railsapp/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/examples/railsapp/app/helpers/posts_helper.rb:
--------------------------------------------------------------------------------
1 | module PostsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/examples/railsapp/app/models/post.rb:
--------------------------------------------------------------------------------
1 | class Post < ApplicationRecord
2 | end
3 |
--------------------------------------------------------------------------------
/examples/railsapp/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/examples/railsapp/bin/dev:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | exec "./bin/rails", "server", *ARGV
3 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source 'https://rubygems.org'
4 |
5 | gemspec
6 |
--------------------------------------------------------------------------------
/examples/railsapp/app/views/posts/show.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.partial! "posts/post", post: @post
2 |
--------------------------------------------------------------------------------
/.github/cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tursodatabase/libsql-activerecord/HEAD/.github/cover.png
--------------------------------------------------------------------------------
/examples/railsapp/.kamal/hooks/docker-setup.sample:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | echo "Docker set up on $KAMAL_HOSTS..."
4 |
--------------------------------------------------------------------------------
/examples/railsapp/app/views/posts/index.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.array! @posts, partial: "posts/post", as: :post
2 |
--------------------------------------------------------------------------------
/examples/railsapp/.kamal/hooks/post-proxy-reboot.sample:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | echo "Rebooted kamal-proxy on $KAMAL_HOSTS"
4 |
--------------------------------------------------------------------------------
/examples/railsapp/.kamal/hooks/pre-proxy-reboot.sample:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | echo "Rebooting kamal-proxy on $KAMAL_HOSTS..."
4 |
--------------------------------------------------------------------------------
/lib/libsql_activerecord.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'active_record/connection_adapters/libsql_adapter'
4 |
--------------------------------------------------------------------------------
/examples/railsapp/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative "../config/boot"
3 | require "rake"
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/examples/railsapp/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | primary_abstract_class
3 | end
4 |
--------------------------------------------------------------------------------
/examples/railsapp/bin/importmap:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require_relative "../config/application"
4 | require "importmap/commands"
5 |
--------------------------------------------------------------------------------
/examples/railsapp/public/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tursodatabase/libsql-activerecord/HEAD/examples/railsapp/public/icon.png
--------------------------------------------------------------------------------
/examples/railsapp/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 |
--------------------------------------------------------------------------------
/examples/railsapp/bin/thrust:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "rubygems"
3 | require "bundler/setup"
4 |
5 | load Gem.bin_path("thruster", "thrust")
6 |
--------------------------------------------------------------------------------
/examples/railsapp/bin/jobs:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require_relative "../config/environment"
4 | require "solid_queue/cli"
5 |
6 | SolidQueue::Cli.start(ARGV)
7 |
--------------------------------------------------------------------------------
/examples/railsapp/public/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/examples/railsapp/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | default from: "from@example.com"
3 | layout "mailer"
4 | end
5 |
--------------------------------------------------------------------------------
/examples/railsapp/app/views/posts/_post.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.extract! post, :id, :name, :title, :content, :created_at, :updated_at
2 | json.url post_url(post, format: :json)
3 |
--------------------------------------------------------------------------------
/examples/railsapp/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path("../config/application", __dir__)
3 | require_relative "../config/boot"
4 | require "rails/commands"
5 |
--------------------------------------------------------------------------------
/examples/railsapp/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative "application"
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/examples/railsapp/test/models/post_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class PostTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/examples/railsapp/bin/brakeman:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "rubygems"
3 | require "bundler/setup"
4 |
5 | ARGV.unshift("--ensure-latest")
6 |
7 | load Gem.bin_path("brakeman", "brakeman")
8 |
--------------------------------------------------------------------------------
/examples/railsapp/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative "config/environment"
4 |
5 | run Rails.application
6 | Rails.application.load_server
7 |
--------------------------------------------------------------------------------
/examples/railsapp/app/javascript/application.js:
--------------------------------------------------------------------------------
1 | // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
2 | import "@hotwired/turbo-rails"
3 | import "controllers"
4 |
--------------------------------------------------------------------------------
/examples/railsapp/test/application_system_test_case.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
4 | driven_by :selenium, using: :headless_chrome, screen_size: [ 1400, 1400 ]
5 | end
6 |
--------------------------------------------------------------------------------
/examples/railsapp/app/javascript/controllers/hello_controller.js:
--------------------------------------------------------------------------------
1 | import { Controller } from "@hotwired/stimulus"
2 |
3 | export default class extends Controller {
4 | connect() {
5 | this.element.textContent = "Hello World!"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/examples/railsapp/app/views/posts/new.html.erb:
--------------------------------------------------------------------------------
1 | <% content_for :title, "New post" %>
2 |
3 |
New post
4 |
5 | <%= render "form", post: @post %>
6 |
7 |
8 |
9 |
10 | <%= link_to "Back to posts", posts_path %>
11 |
12 |
--------------------------------------------------------------------------------
/examples/railsapp/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
2 |
3 | require "bundler/setup" # Set up gems listed in the Gemfile.
4 | require "bootsnap/setup" # Speed up boot time by caching expensive operations.
5 |
--------------------------------------------------------------------------------
/examples/railsapp/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require_relative "config/application"
5 |
6 | Rails.application.load_tasks
7 |
--------------------------------------------------------------------------------
/examples/railsapp/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
3 | allow_browser versions: :modern
4 | end
5 |
--------------------------------------------------------------------------------
/examples/railsapp/test/fixtures/posts.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | one:
4 | name: MyString
5 | title: MyString
6 | content: MyText
7 |
8 | two:
9 | name: MyString
10 | title: MyString
11 | content: MyText
12 |
--------------------------------------------------------------------------------
/examples/railsapp/.rubocop.yml:
--------------------------------------------------------------------------------
1 | # Omakase Ruby styling for Rails
2 | inherit_gem: { rubocop-rails-omakase: rubocop.yml }
3 |
4 | # Overwrite or add rules to create your own house style
5 | #
6 | # # Use `[a, [b, c]]` not `[ a, [ b, c ] ]`
7 | # Layout/SpaceInsideArrayLiteralBrackets:
8 | # Enabled: false
9 |
--------------------------------------------------------------------------------
/examples/railsapp/db/migrate/20241216101156_create_posts.rb:
--------------------------------------------------------------------------------
1 | class CreatePosts < ActiveRecord::Migration[8.0]
2 | def change
3 | create_table :posts do |t|
4 | t.string :name
5 | t.string :title
6 | t.text :content
7 |
8 | t.timestamps
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/examples/railsapp/app/views/posts/edit.html.erb:
--------------------------------------------------------------------------------
1 | <% content_for :title, "Editing post" %>
2 |
3 | Editing post
4 |
5 | <%= render "form", post: @post %>
6 |
7 |
8 |
9 |
10 | <%= link_to "Show this post", @post %> |
11 | <%= link_to "Back to posts", posts_path %>
12 |
13 |
--------------------------------------------------------------------------------
/examples/railsapp/app/javascript/controllers/application.js:
--------------------------------------------------------------------------------
1 | import { Application } from "@hotwired/stimulus"
2 |
3 | const application = Application.start()
4 |
5 | // Configure Stimulus development experience
6 | application.debug = false
7 | window.Stimulus = application
8 |
9 | export { application }
10 |
--------------------------------------------------------------------------------
/examples/railsapp/bin/rubocop:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "rubygems"
3 | require "bundler/setup"
4 |
5 | # explicit rubocop config increases performance slightly while avoiding config confusion.
6 | ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__))
7 |
8 | load Gem.bin_path("rubocop", "rubocop")
9 |
--------------------------------------------------------------------------------
/examples/railsapp/app/javascript/controllers/index.js:
--------------------------------------------------------------------------------
1 | // Import and register all your controllers from the importmap via controllers/**/*_controller
2 | import { application } from "controllers/application"
3 | import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
4 | eagerLoadControllersFrom("controllers", application)
5 |
--------------------------------------------------------------------------------
/examples/railsapp/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | class ApplicationJob < ActiveJob::Base
2 | # Automatically retry jobs that encountered a deadlock
3 | # retry_on ActiveRecord::Deadlocked
4 |
5 | # Most jobs are safe to ignore if the underlying records are no longer available
6 | # discard_on ActiveJob::DeserializationError
7 | end
8 |
--------------------------------------------------------------------------------
/examples/railsapp/app/views/posts/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= notice %>
2 |
3 | <%= render @post %>
4 |
5 |
6 | <%= link_to "Edit this post", edit_post_path(@post) %> |
7 | <%= link_to "Back to posts", posts_path %>
8 |
9 | <%= button_to "Destroy this post", @post, method: :delete %>
10 |
11 |
--------------------------------------------------------------------------------
/examples/railsapp/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: bundler
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 | - package-ecosystem: github-actions
9 | directory: "/"
10 | schedule:
11 | interval: daily
12 | open-pull-requests-limit: 10
13 |
--------------------------------------------------------------------------------
/examples/railsapp/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/railsapp/config/importmap.rb:
--------------------------------------------------------------------------------
1 | # Pin npm packages by running ./bin/importmap
2 |
3 | pin "application"
4 | pin "@hotwired/turbo-rails", to: "turbo.min.js"
5 | pin "@hotwired/stimulus", to: "stimulus.min.js"
6 | pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
7 | pin_all_from "app/javascript/controllers", under: "controllers"
8 |
--------------------------------------------------------------------------------
/examples/railsapp/app/views/posts/_post.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | Name:
4 | <%= post.name %>
5 |
6 |
7 |
8 | Title:
9 | <%= post.title %>
10 |
11 |
12 |
13 | Content:
14 | <%= post.content %>
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/examples/railsapp/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = "1.0"
5 |
6 | # Add additional assets to the asset load path.
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 |
--------------------------------------------------------------------------------
/examples/railsapp/config/recurring.yml:
--------------------------------------------------------------------------------
1 | # production:
2 | # periodic_cleanup:
3 | # class: CleanSoftDeletedRecordsJob
4 | # queue: background
5 | # args: [ 1000, { batch_size: 500 } ]
6 | # schedule: every hour
7 | # periodic_command:
8 | # command: "SoftDeletedRecord.due.delete_all"
9 | # priority: 2
10 | # schedule: at 5am every day
11 |
--------------------------------------------------------------------------------
/examples/railsapp/app/views/posts/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= notice %>
2 |
3 | <% content_for :title, "Posts" %>
4 |
5 | Posts
6 |
7 |
8 | <% @posts.each do |post| %>
9 | <%= render post %>
10 |
11 | <%= link_to "Show this post", post %>
12 |
13 | <% end %>
14 |
15 |
16 | <%= link_to "New post", new_post_path %>
17 |
--------------------------------------------------------------------------------
/examples/railsapp/config/cache.yml:
--------------------------------------------------------------------------------
1 | default: &default
2 | store_options:
3 | # Cap age of oldest cache entry to fulfill retention policies
4 | # max_age: <%= 60.days.to_i %>
5 | max_size: <%= 256.megabytes %>
6 | namespace: <%= Rails.env %>
7 |
8 | development:
9 | <<: *default
10 |
11 | test:
12 | <<: *default
13 |
14 | production:
15 | database: cache
16 | <<: *default
17 |
--------------------------------------------------------------------------------
/examples/railsapp/config/queue.yml:
--------------------------------------------------------------------------------
1 | default: &default
2 | dispatchers:
3 | - polling_interval: 1
4 | batch_size: 500
5 | workers:
6 | - queues: "*"
7 | threads: 3
8 | processes: <%= ENV.fetch("JOB_CONCURRENCY", 1) %>
9 | polling_interval: 0.1
10 |
11 | development:
12 | <<: *default
13 |
14 | test:
15 | <<: *default
16 |
17 | production:
18 | <<: *default
19 |
--------------------------------------------------------------------------------
/examples/railsapp/.gitattributes:
--------------------------------------------------------------------------------
1 | # See https://git-scm.com/docs/gitattributes for more about git attribute files.
2 |
3 | # Mark the database schema as having been generated.
4 | db/schema.rb linguist-generated
5 |
6 | # Mark any vendored files as having been vendored.
7 | vendor/* linguist-vendored
8 | config/credentials/*.yml.enc diff=rails_credentials
9 | config/credentials.yml.enc diff=rails_credentials
10 |
--------------------------------------------------------------------------------
/examples/railsapp/.kamal/hooks/post-deploy.sample:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # A sample post-deploy hook
4 | #
5 | # These environment variables are available:
6 | # KAMAL_RECORDED_AT
7 | # KAMAL_PERFORMER
8 | # KAMAL_VERSION
9 | # KAMAL_HOSTS
10 | # KAMAL_ROLE (if set)
11 | # KAMAL_DESTINATION (if set)
12 | # KAMAL_RUNTIME
13 |
14 | echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds"
15 |
--------------------------------------------------------------------------------
/examples/railsapp/bin/docker-entrypoint:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | # Enable jemalloc for reduced memory usage and latency.
4 | if [ -z "${LD_PRELOAD+x}" ]; then
5 | LD_PRELOAD=$(find /usr/lib -name libjemalloc.so.2 -print -quit)
6 | export LD_PRELOAD
7 | fi
8 |
9 | # If running the rails server then create or migrate existing database
10 | if [ "${@: -2:1}" == "./bin/rails" ] && [ "${@: -1:1}" == "server" ]; then
11 | ./bin/rails db:prepare
12 | fi
13 |
14 | exec "${@}"
15 |
--------------------------------------------------------------------------------
/examples/railsapp/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | ENV["RAILS_ENV"] ||= "test"
2 | require_relative "../config/environment"
3 | require "rails/test_help"
4 |
5 | module ActiveSupport
6 | class TestCase
7 | # Run tests in parallel with specified workers
8 | parallelize(workers: :number_of_processors)
9 |
10 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
11 | fixtures :all
12 |
13 | # Add more helper methods to be used by all tests here...
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/examples/railsapp/app/views/pwa/manifest.json.erb:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Railsapp",
3 | "icons": [
4 | {
5 | "src": "/icon.png",
6 | "type": "image/png",
7 | "sizes": "512x512"
8 | },
9 | {
10 | "src": "/icon.png",
11 | "type": "image/png",
12 | "sizes": "512x512",
13 | "purpose": "maskable"
14 | }
15 | ],
16 | "start_url": "/",
17 | "display": "standalone",
18 | "scope": "/",
19 | "description": "Railsapp.",
20 | "theme_color": "red",
21 | "background_color": "red"
22 | }
23 |
--------------------------------------------------------------------------------
/examples/railsapp/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file.
4 | # Use this to limit dissemination of sensitive information.
5 | # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors.
6 | Rails.application.config.filter_parameters += [
7 | :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc
8 | ]
9 |
--------------------------------------------------------------------------------
/examples/railsapp/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should ensure the existence of records required to run the application in every environment (production,
2 | # development, test). The code here should be idempotent so that it can be executed at any point in every environment.
3 | # The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup).
4 | #
5 | # Example:
6 | #
7 | # ["Action", "Comedy", "Drama", "Horror"].each do |genre_name|
8 | # MovieGenre.find_or_create_by!(name: genre_name)
9 | # end
10 |
--------------------------------------------------------------------------------
/examples/railsapp/config/credentials.yml.enc:
--------------------------------------------------------------------------------
1 | 9F14yCZeLwc0CSlJfIDk6GEP+CRbvX5tt/HSzakrwRmktuhBsAAy5TJuth5dMYCwksPz86ZKL4NNBESz3FSeHdLxKJ8ayeHEkMysdcUfuGxJNUGpbODg5XdfKIaGRoSUeHlX/rbFh+n9dC4DLdPLgLpynAGD86oD7QGEqrhr+YGSCaYMrDrYNUa1ypOeMcUVxusoa0uBHe3Z/YIMuZ1ZQIk0jfSOaBCtrTJefaUNYz+4aJtcTHa4PJFnu3BBhF1ppcyT79u0CAOhWeif7ZW1LKZlKZJzkUMqzTpIxC8YnzVwA7NEFnqM2lgyhFoUh1rg4u9xqH9lXRTAZWjaLfreHeAn7YZXOmkkHvAyZV1D8/Gb2DlqtCDskyW8zApDiMv7nOY03hNjCBBkZPmZNKDBQ0W+hx9wk7HxQn7Fks6mbMJD+dj3stv1SBzP2DbsUXez0HSeGhR4z4BVKQ6oT2JW293pZt8Ydd8IE2k4SvXO7BiXFds5APYllaUc--7GH34fXi3sXGp+X+--1NF6zBYjPsFZNLXkZKNgpw==
--------------------------------------------------------------------------------
/examples/railsapp/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css.
3 | *
4 | * With Propshaft, assets are served efficiently without preprocessing steps. You can still include
5 | * application-wide styles in this file, but keep in mind that CSS precedence will follow the standard
6 | * cascading order, meaning styles declared later in the document or manifest will override earlier ones,
7 | * depending on specificity.
8 | *
9 | * Consider organizing styles into separate files for maintainability.
10 | */
11 |
--------------------------------------------------------------------------------
/examples/railsapp/db/cable_schema.rb:
--------------------------------------------------------------------------------
1 | ActiveRecord::Schema[7.1].define(version: 1) do
2 | create_table "solid_cable_messages", force: :cascade do |t|
3 | t.binary "channel", limit: 1024, null: false
4 | t.binary "payload", limit: 536870912, null: false
5 | t.datetime "created_at", null: false
6 | t.integer "channel_hash", limit: 8, null: false
7 | t.index ["channel"], name: "index_solid_cable_messages_on_channel"
8 | t.index ["channel_hash"], name: "index_solid_cable_messages_on_channel_hash"
9 | t.index ["created_at"], name: "index_solid_cable_messages_on_created_at"
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/examples/railsapp/config/cable.yml:
--------------------------------------------------------------------------------
1 | # Async adapter only works within the same process, so for manually triggering cable updates from a console,
2 | # and seeing results in the browser, you must do so from the web console (running inside the dev process),
3 | # not a terminal started via bin/rails console! Add "console" to any action or any ERB template view
4 | # to make the web console appear.
5 | development:
6 | adapter: async
7 |
8 | test:
9 | adapter: test
10 |
11 | production:
12 | adapter: solid_cable
13 | connects_to:
14 | database:
15 | writing: cable
16 | polling_interval: 0.1.seconds
17 | message_retention: 1.day
18 |
--------------------------------------------------------------------------------
/libsql_activerecord.gemspec:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Gem::Specification.new do |spec|
4 | spec.name = 'libsql_activerecord'
5 | spec.version = '0.0.2'
6 | spec.authors = ['Levy A.']
7 | spec.email = ['levyddsa@gmail.com']
8 | spec.summary = 'libSQL ActiveRecord Adapter'
9 | spec.homepage = 'https://rubygems.org/gems/libsql_activerecord'
10 | spec.files = Dir['lib/**/*']
11 |
12 | spec.license = 'MIT'
13 | spec.required_ruby_version = '>= 3.3'
14 |
15 | spec.require_paths = ['lib']
16 |
17 | spec.add_runtime_dependency 'activerecord', '~> 8.0'
18 | spec.add_runtime_dependency 'turso_libsql', '~> 0.2'
19 | end
20 |
--------------------------------------------------------------------------------
/examples/railsapp/README.md:
--------------------------------------------------------------------------------
1 | # Rails + libSQL ActiveRecord Example
2 |
3 | This example demonstrates how to use libSQL with a Ruby on Rails.
4 |
5 | ## Install Dependencies
6 |
7 | ```bash
8 | bundle
9 | ```
10 |
11 | ## Configure database
12 |
13 | Create a database, auth token and configure it inside `.env.development`:
14 |
15 | ```bash
16 | TURSO_DATABASE_URL=libsql://
17 | TURSO_DATABASE_AUTH_TOKEN=
18 | ```
19 |
20 | ## Migrate the database
21 |
22 | Run the following to migrate the database:
23 |
24 | ```bash
25 | bin/rails db:migrate
26 | ```
27 |
28 | ## Running
29 |
30 | Execute the example:
31 |
32 | ```bash
33 | bin/rails server
34 | ```
35 |
--------------------------------------------------------------------------------
/examples/railsapp/db/cache_schema.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | ActiveRecord::Schema[7.2].define(version: 1) do
4 | create_table "solid_cache_entries", force: :cascade do |t|
5 | t.binary "key", limit: 1024, null: false
6 | t.binary "value", limit: 536870912, null: false
7 | t.datetime "created_at", null: false
8 | t.integer "key_hash", limit: 8, null: false
9 | t.integer "byte_size", limit: 4, null: false
10 | t.index ["byte_size"], name: "index_solid_cache_entries_on_byte_size"
11 | t.index ["key_hash", "byte_size"], name: "index_solid_cache_entries_on_key_hash_and_byte_size"
12 | t.index ["key_hash"], name: "index_solid_cache_entries_on_key_hash", unique: true
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/examples/railsapp/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, "\\1en"
8 | # inflect.singular /^(ox)en/i, "\\1"
9 | # inflect.irregular "person", "people"
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym "RESTful"
16 | # end
17 |
--------------------------------------------------------------------------------
/examples/railsapp/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | resources :posts
3 | # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
4 |
5 | # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
6 | # Can be used by load balancers and uptime monitors to verify that the app is live.
7 | get "up" => "rails/health#show", as: :rails_health_check
8 |
9 | # Render dynamic PWA files from app/views/pwa/* (remember to link manifest in application.html.erb)
10 | # get "manifest" => "rails/pwa#manifest", as: :pwa_manifest
11 | # get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker
12 |
13 | # Defines the root path route ("/")
14 | # root "posts#index"
15 | end
16 |
--------------------------------------------------------------------------------
/examples/memory.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'libsql_activerecord'
4 | require 'active_record'
5 |
6 | ActiveRecord::Base.establish_connection(adapter: 'libsql')
7 |
8 | class Product < ActiveRecord::Base
9 | end
10 |
11 | class CreateProducts < ActiveRecord::Migration[8.0]
12 | def change
13 | create_table :products do |t|
14 | t.string :name
15 | t.timestamps
16 | end
17 | end
18 | end
19 |
20 | class AddDescription < ActiveRecord::Migration[8.0]
21 | def change
22 | add_column :products, :description, :string
23 | end
24 | end
25 |
26 | CreateProducts.migrate(:up)
27 | AddDescription.migrate(:up)
28 |
29 | product = Product.create
30 | product.name = 'Book'
31 | product.description = 'A book about books'
32 | product.save
33 |
34 | Product.all.each do |product|
35 | product.destroy
36 | end
37 |
38 | p Product.all
39 |
--------------------------------------------------------------------------------
/examples/railsapp/bin/kamal:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'kamal' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12 |
13 | bundle_binstub = File.expand_path("bundle", __dir__)
14 |
15 | if File.file?(bundle_binstub)
16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17 | load(bundle_binstub)
18 | else
19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21 | end
22 | end
23 |
24 | require "rubygems"
25 | require "bundler/setup"
26 |
27 | load Gem.bin_path("kamal", "kamal")
28 |
--------------------------------------------------------------------------------
/examples/railsapp/app/views/posts/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with(model: post) do |form| %>
2 | <% if post.errors.any? %>
3 |
4 |
<%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:
5 |
6 |
7 | <% post.errors.each do |error| %>
8 | <%= error.full_message %>
9 | <% end %>
10 |
11 |
12 | <% end %>
13 |
14 |
15 | <%= form.label :name, style: "display: block" %>
16 | <%= form.text_field :name %>
17 |
18 |
19 |
20 | <%= form.label :title, style: "display: block" %>
21 | <%= form.text_field :title %>
22 |
23 |
24 |
25 | <%= form.label :content, style: "display: block" %>
26 | <%= form.textarea :content %>
27 |
28 |
29 |
30 | <%= form.submit %>
31 |
32 | <% end %>
33 |
--------------------------------------------------------------------------------
/examples/railsapp/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2 | #
3 | # Temporary files generated by your text editor or operating system
4 | # belong in git's global ignore instead:
5 | # `$XDG_CONFIG_HOME/git/ignore` or `~/.config/git/ignore`
6 |
7 | # Ignore bundler config.
8 | /.bundle
9 |
10 | # Ignore all environment files.
11 | /.env*
12 |
13 | # Ignore all logfiles and tempfiles.
14 | /log/*
15 | /tmp/*
16 | !/log/.keep
17 | !/tmp/.keep
18 |
19 | # Ignore pidfiles, but keep the directory.
20 | /tmp/pids/*
21 | !/tmp/pids/
22 | !/tmp/pids/.keep
23 |
24 | # Ignore storage (uploaded files in development and any SQLite databases).
25 | /storage/*
26 | !/storage/.keep
27 | /tmp/storage/*
28 | !/tmp/storage/
29 | !/tmp/storage/.keep
30 |
31 | /public/assets
32 |
33 | # Ignore master key for decrypting credentials and more.
34 | /config/master.key
35 |
--------------------------------------------------------------------------------
/examples/railsapp/app/views/pwa/service-worker.js:
--------------------------------------------------------------------------------
1 | // Add a service worker for processing Web Push notifications:
2 | //
3 | // self.addEventListener("push", async (event) => {
4 | // const { title, options } = await event.data.json()
5 | // event.waitUntil(self.registration.showNotification(title, options))
6 | // })
7 | //
8 | // self.addEventListener("notificationclick", function(event) {
9 | // event.notification.close()
10 | // event.waitUntil(
11 | // clients.matchAll({ type: "window" }).then((clientList) => {
12 | // for (let i = 0; i < clientList.length; i++) {
13 | // let client = clientList[i]
14 | // let clientPath = (new URL(client.url)).pathname
15 | //
16 | // if (clientPath == event.notification.data.path && "focus" in client) {
17 | // return client.focus()
18 | // }
19 | // }
20 | //
21 | // if (clients.openWindow) {
22 | // return clients.openWindow(event.notification.data.path)
23 | // }
24 | // })
25 | // )
26 | // })
27 |
--------------------------------------------------------------------------------
/examples/railsapp/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization and
2 | # are automatically loaded by Rails. If you want to use locales other than
3 | # English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t "hello"
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t("hello") %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # To learn more about the API, please read the Rails Internationalization guide
20 | # at https://guides.rubyonrails.org/i18n.html.
21 | #
22 | # Be aware that YAML interprets the following case-insensitive strings as
23 | # booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings
24 | # must be quoted to be interpreted as strings. For example:
25 | #
26 | # en:
27 | # "yes": yup
28 | # enabled: "ON"
29 |
30 | en:
31 | hello: "Hello world"
32 |
--------------------------------------------------------------------------------
/examples/railsapp/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= content_for(:title) || "Railsapp" %>
5 |
6 |
7 |
8 | <%= csrf_meta_tags %>
9 | <%= csp_meta_tag %>
10 |
11 | <%= yield :head %>
12 |
13 | <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
14 | <%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>
15 |
16 |
17 |
18 |
19 |
20 | <%# Includes all stylesheet files in app/assets/stylesheets %>
21 | <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
22 | <%= javascript_importmap_tags %>
23 |
24 |
25 |
26 | <%= yield %>
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/railsapp/.kamal/secrets:
--------------------------------------------------------------------------------
1 | # Secrets defined here are available for reference under registry/password, env/secret, builder/secrets,
2 | # and accessories/*/env/secret in config/deploy.yml. All secrets should be pulled from either
3 | # password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git.
4 |
5 | # Example of extracting secrets from 1password (or another compatible pw manager)
6 | # SECRETS=$(kamal secrets fetch --adapter 1password --account your-account --from Vault/Item KAMAL_REGISTRY_PASSWORD RAILS_MASTER_KEY)
7 | # KAMAL_REGISTRY_PASSWORD=$(kamal secrets extract KAMAL_REGISTRY_PASSWORD ${SECRETS})
8 | # RAILS_MASTER_KEY=$(kamal secrets extract RAILS_MASTER_KEY ${SECRETS})
9 |
10 | # Use a GITHUB_TOKEN if private repositories are needed for the image
11 | # GITHUB_TOKEN=$(gh config get -h github.com oauth_token)
12 |
13 | # Grab the registry password from ENV
14 | KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
15 |
16 | # Improve security by using a password manager. Never check config/master.key into git!
17 | RAILS_MASTER_KEY=$(cat config/master.key)
18 |
--------------------------------------------------------------------------------
/examples/railsapp/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # This file is the source Rails uses to define your schema when running `bin/rails
6 | # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
7 | # be faster and is potentially less error prone than running all of your
8 | # migrations from scratch. Old migrations may fail to apply correctly if those
9 | # migrations use external dependencies or application code.
10 | #
11 | # It's strongly recommended that you check this file into your version control system.
12 |
13 | ActiveRecord::Schema[8.0].define(version: 2024_12_16_101156) do
14 | create_table "posts", id: :integer, force: :cascade do |t|
15 | t.string "name"
16 | t.string "title"
17 | t.text "content"
18 | t.datetime "created_at", precision: nil, null: false
19 | t.datetime "updated_at", precision: nil, null: false
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/examples/railsapp/.dockerignore:
--------------------------------------------------------------------------------
1 | # See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files.
2 |
3 | # Ignore git directory.
4 | /.git/
5 | /.gitignore
6 |
7 | # Ignore bundler config.
8 | /.bundle
9 |
10 | # Ignore all environment files.
11 | /.env*
12 |
13 | # Ignore all default key files.
14 | /config/master.key
15 | /config/credentials/*.key
16 |
17 | # Ignore all logfiles and tempfiles.
18 | /log/*
19 | /tmp/*
20 | !/log/.keep
21 | !/tmp/.keep
22 |
23 | # Ignore pidfiles, but keep the directory.
24 | /tmp/pids/*
25 | !/tmp/pids/.keep
26 |
27 | # Ignore storage (uploaded files in development and any SQLite databases).
28 | /storage/*
29 | !/storage/.keep
30 | /tmp/storage/*
31 | !/tmp/storage/.keep
32 |
33 | # Ignore assets.
34 | /node_modules/
35 | /app/assets/builds/*
36 | !/app/assets/builds/.keep
37 | /public/assets
38 |
39 | # Ignore CI service files.
40 | /.github
41 |
42 | # Ignore Kamal files.
43 | /config/deploy*.yml
44 | /.kamal
45 |
46 | # Ignore development files
47 | /.devcontainer
48 |
49 | # Ignore Docker-related files
50 | /.dockerignore
51 | /Dockerfile*
52 |
--------------------------------------------------------------------------------
/examples/railsapp/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative "boot"
2 |
3 | require "rails/all"
4 |
5 | # Require the gems listed in Gemfile, including any gems
6 | # you've limited to :test, :development, or :production.
7 | Bundler.require(*Rails.groups)
8 |
9 | module Railsapp
10 | class Application < Rails::Application
11 | # Initialize configuration defaults for originally generated Rails version.
12 | config.load_defaults 8.0
13 |
14 | # Please, add to the `ignore` list any other `lib` subdirectories that do
15 | # not contain `.rb` files, or that should not be reloaded or eager loaded.
16 | # Common ones are `templates`, `generators`, or `middleware`, for example.
17 | config.autoload_lib(ignore: %w[assets tasks])
18 |
19 | # Configuration for the application, engines, and railties goes here.
20 | #
21 | # These settings can be overridden in specific environments using the files
22 | # in config/environments, which are processed later.
23 | #
24 | # config.time_zone = "Central Time (US & Canada)"
25 | # config.eager_load_paths << Rails.root.join("extras")
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Turso (libSQL)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/examples/railsapp/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "fileutils"
3 |
4 | APP_ROOT = File.expand_path("..", __dir__)
5 |
6 | def system!(*args)
7 | system(*args, exception: true)
8 | end
9 |
10 | FileUtils.chdir APP_ROOT do
11 | # This script is a way to set up or update your development environment automatically.
12 | # This script is idempotent, so that you can run it at any time and get an expectable outcome.
13 | # Add necessary setup steps to this file.
14 |
15 | puts "== Installing dependencies =="
16 | system("bundle check") || system!("bundle install")
17 |
18 | # puts "\n== Copying sample files =="
19 | # unless File.exist?("config/database.yml")
20 | # FileUtils.cp "config/database.yml.sample", "config/database.yml"
21 | # end
22 |
23 | puts "\n== Preparing database =="
24 | system! "bin/rails db:prepare"
25 |
26 | puts "\n== Removing old logs and tempfiles =="
27 | system! "bin/rails log:clear tmp:clear"
28 |
29 | unless ARGV.include?("--skip-server")
30 | puts "\n== Starting development server =="
31 | STDOUT.flush # flush the output before exec(2) so that it displays
32 | exec "bin/dev"
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/examples/railsapp/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Define an application-wide content security policy.
4 | # See the Securing Rails Applications Guide for more information:
5 | # https://guides.rubyonrails.org/security.html#content-security-policy-header
6 |
7 | # Rails.application.configure do
8 | # config.content_security_policy do |policy|
9 | # policy.default_src :self, :https
10 | # policy.font_src :self, :https, :data
11 | # policy.img_src :self, :https, :data
12 | # policy.object_src :none
13 | # policy.script_src :self, :https
14 | # policy.style_src :self, :https
15 | # # Specify URI for violation reports
16 | # # policy.report_uri "/csp-violation-report-endpoint"
17 | # end
18 | #
19 | # # Generate session nonces for permitted importmap, inline scripts, and inline styles.
20 | # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
21 | # config.content_security_policy_nonce_directives = %w(script-src style-src)
22 | #
23 | # # Report violations without enforcing the policy.
24 | # # config.content_security_policy_report_only = true
25 | # end
26 |
--------------------------------------------------------------------------------
/examples/railsapp/test/system/posts_test.rb:
--------------------------------------------------------------------------------
1 | require "application_system_test_case"
2 |
3 | class PostsTest < ApplicationSystemTestCase
4 | setup do
5 | @post = posts(:one)
6 | end
7 |
8 | test "visiting the index" do
9 | visit posts_url
10 | assert_selector "h1", text: "Posts"
11 | end
12 |
13 | test "should create post" do
14 | visit posts_url
15 | click_on "New post"
16 |
17 | fill_in "Content", with: @post.content
18 | fill_in "Name", with: @post.name
19 | fill_in "Title", with: @post.title
20 | click_on "Create Post"
21 |
22 | assert_text "Post was successfully created"
23 | click_on "Back"
24 | end
25 |
26 | test "should update Post" do
27 | visit post_url(@post)
28 | click_on "Edit this post", match: :first
29 |
30 | fill_in "Content", with: @post.content
31 | fill_in "Name", with: @post.name
32 | fill_in "Title", with: @post.title
33 | click_on "Update Post"
34 |
35 | assert_text "Post was successfully updated"
36 | click_on "Back"
37 | end
38 |
39 | test "should destroy Post" do
40 | visit post_url(@post)
41 | click_on "Destroy this post", match: :first
42 |
43 | assert_text "Post was successfully destroyed"
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/examples/railsapp/.kamal/hooks/pre-connect.sample:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # A sample pre-connect check
4 | #
5 | # Warms DNS before connecting to hosts in parallel
6 | #
7 | # These environment variables are available:
8 | # KAMAL_RECORDED_AT
9 | # KAMAL_PERFORMER
10 | # KAMAL_VERSION
11 | # KAMAL_HOSTS
12 | # KAMAL_ROLE (if set)
13 | # KAMAL_DESTINATION (if set)
14 | # KAMAL_RUNTIME
15 |
16 | hosts = ENV["KAMAL_HOSTS"].split(",")
17 | results = nil
18 | max = 3
19 |
20 | elapsed = Benchmark.realtime do
21 | results = hosts.map do |host|
22 | Thread.new do
23 | tries = 1
24 |
25 | begin
26 | Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)
27 | rescue SocketError
28 | if tries < max
29 | puts "Retrying DNS warmup: #{host}"
30 | tries += 1
31 | sleep rand
32 | retry
33 | else
34 | puts "DNS warmup failed: #{host}"
35 | host
36 | end
37 | end
38 |
39 | tries
40 | end
41 | end.map(&:value)
42 | end
43 |
44 | retries = results.sum - hosts.size
45 | nopes = results.count { |r| r == max }
46 |
47 | puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ]
48 |
--------------------------------------------------------------------------------
/examples/railsapp/test/controllers/posts_controller_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class PostsControllerTest < ActionDispatch::IntegrationTest
4 | setup do
5 | @post = posts(:one)
6 | end
7 |
8 | test "should get index" do
9 | get posts_url
10 | assert_response :success
11 | end
12 |
13 | test "should get new" do
14 | get new_post_url
15 | assert_response :success
16 | end
17 |
18 | test "should create post" do
19 | assert_difference("Post.count") do
20 | post posts_url, params: { post: { content: @post.content, name: @post.name, title: @post.title } }
21 | end
22 |
23 | assert_redirected_to post_url(Post.last)
24 | end
25 |
26 | test "should show post" do
27 | get post_url(@post)
28 | assert_response :success
29 | end
30 |
31 | test "should get edit" do
32 | get edit_post_url(@post)
33 | assert_response :success
34 | end
35 |
36 | test "should update post" do
37 | patch post_url(@post), params: { post: { content: @post.content, name: @post.name, title: @post.title } }
38 | assert_redirected_to post_url(@post)
39 | end
40 |
41 | test "should destroy post" do
42 | assert_difference("Post.count", -1) do
43 | delete post_url(@post)
44 | end
45 |
46 | assert_redirected_to posts_url
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/examples/railsapp/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket-<%= Rails.env %>
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket-<%= Rails.env %>
23 |
24 | # Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name-<%= Rails.env %>
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | description = "A very basic flake";
3 |
4 | inputs = {
5 | nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
6 | ruby-nix.url = "github:inscapist/ruby-nix";
7 | bundix = {
8 | url = "github:inscapist/bundix/main";
9 | inputs.nixpkgs.follows = "nixpkgs";
10 | };
11 | flake-utils = {
12 | url = "github:numtide/flake-utils";
13 | };
14 | };
15 | outputs = { self, nixpkgs, flake-utils, ruby-nix, bundix }:
16 | flake-utils.lib.eachDefaultSystem (system:
17 | let
18 | pkgs = import nixpkgs {
19 | inherit system;
20 | # overlays = [ ruby-nix.overlays.ruby ];
21 | };
22 | rubyNix = ruby-nix.lib pkgs;
23 |
24 | inherit (rubyNix {
25 | gemset = if builtins.pathExists ./gemset.nix then import ./gemset.nix else { };
26 | name = "ruby-env";
27 | ruby = pkgs.ruby_3_3;
28 | }) env ruby;
29 | in
30 | {
31 | devShells.default =
32 | with pkgs;
33 | mkShell {
34 | buildInputs = [
35 | bundix.packages.${system}.default
36 | turso-cli
37 |
38 | env
39 | ruby
40 | ] ++ lib.optionals stdenv.isDarwin [ iconv ];
41 | };
42 | });
43 | }
44 |
45 |
46 |
--------------------------------------------------------------------------------
/examples/railsapp/.kamal/hooks/pre-build.sample:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # A sample pre-build hook
4 | #
5 | # Checks:
6 | # 1. We have a clean checkout
7 | # 2. A remote is configured
8 | # 3. The branch has been pushed to the remote
9 | # 4. The version we are deploying matches the remote
10 | #
11 | # These environment variables are available:
12 | # KAMAL_RECORDED_AT
13 | # KAMAL_PERFORMER
14 | # KAMAL_VERSION
15 | # KAMAL_HOSTS
16 | # KAMAL_ROLE (if set)
17 | # KAMAL_DESTINATION (if set)
18 |
19 | if [ -n "$(git status --porcelain)" ]; then
20 | echo "Git checkout is not clean, aborting..." >&2
21 | git status --porcelain >&2
22 | exit 1
23 | fi
24 |
25 | first_remote=$(git remote)
26 |
27 | if [ -z "$first_remote" ]; then
28 | echo "No git remote set, aborting..." >&2
29 | exit 1
30 | fi
31 |
32 | current_branch=$(git branch --show-current)
33 |
34 | if [ -z "$current_branch" ]; then
35 | echo "Not on a git branch, aborting..." >&2
36 | exit 1
37 | fi
38 |
39 | remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1)
40 |
41 | if [ -z "$remote_head" ]; then
42 | echo "Branch not pushed to remote, aborting..." >&2
43 | exit 1
44 | fi
45 |
46 | if [ "$KAMAL_VERSION" != "$remote_head" ]; then
47 | echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
48 | exit 1
49 | fi
50 |
51 | exit 0
52 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | libsql_activerecord (0.0.2)
5 | activerecord (~> 8.0)
6 | turso_libsql (~> 0.2)
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | specs:
11 | activemodel (8.0.1)
12 | activesupport (= 8.0.1)
13 | activerecord (8.0.1)
14 | activemodel (= 8.0.1)
15 | activesupport (= 8.0.1)
16 | timeout (>= 0.4.0)
17 | activesupport (8.0.1)
18 | base64
19 | benchmark (>= 0.3)
20 | bigdecimal
21 | concurrent-ruby (~> 1.0, >= 1.3.1)
22 | connection_pool (>= 2.2.5)
23 | drb
24 | i18n (>= 1.6, < 2)
25 | logger (>= 1.4.2)
26 | minitest (>= 5.1)
27 | securerandom (>= 0.3)
28 | tzinfo (~> 2.0, >= 2.0.5)
29 | uri (>= 0.13.1)
30 | base64 (0.2.0)
31 | benchmark (0.4.0)
32 | bigdecimal (3.1.8)
33 | concurrent-ruby (1.3.4)
34 | connection_pool (2.4.1)
35 | drb (2.2.1)
36 | ffi (1.17.0)
37 | ffi (1.17.0-arm64-darwin)
38 | i18n (1.14.6)
39 | concurrent-ruby (~> 1.0)
40 | logger (1.6.3)
41 | minitest (5.25.4)
42 | securerandom (0.4.1)
43 | timeout (0.4.3)
44 | turso_libsql (0.2.0)
45 | ffi (~> 1.17)
46 | tzinfo (2.0.6)
47 | concurrent-ruby (~> 1.0)
48 | uri (1.0.2)
49 |
50 | PLATFORMS
51 | arm64-darwin-23
52 | ruby
53 |
54 | DEPENDENCIES
55 | libsql_activerecord!
56 |
57 | BUNDLED WITH
58 | 2.5.16
59 |
--------------------------------------------------------------------------------
/examples/railsapp/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite. Versions 3.8.0 and up are supported.
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem "sqlite3"
6 | #
7 | default: &default
8 | adapter: libsql
9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | url: "libsql://railsdemo-notrab.turso.io"
15 | auth_token: "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MzQzNDM4ODUsImlkIjoiZTJhY2MwODgtNmViYi00ZjYyLWIwNWEtZDcwNjEyYzI4YzcxIn0.MTqTi2_X6wqPqNHa_jEomJ1LnzhwyA23Qzw_t2X92t9ljW_Yf2PsywFkx8ZLFuW6LAI2UEU3jK0Px7mTuSnBCA"
16 | path: storage/embedded_replica.turso
17 |
18 | # Warning: The database defined as "test" will be erased and
19 | # re-generated from your development database when you run "rake".
20 | # Do not set this db to the same as development or production.
21 | test:
22 | <<: *default
23 | database: storage/test.sqlite3
24 |
25 | # Store production database in the storage/ directory, which by default
26 | # is mounted as a persistent Docker volume in config/deploy.yml.
27 | production:
28 | primary:
29 | <<: *default
30 | database: storage/production.sqlite3
31 | cache:
32 | <<: *default
33 | database: storage/production_cache.sqlite3
34 | migrations_paths: db/cache_migrate
35 | queue:
36 | <<: *default
37 | database: storage/production_queue.sqlite3
38 | migrations_paths: db/queue_migrate
39 | cable:
40 | <<: *default
41 | database: storage/production_cable.sqlite3
42 | migrations_paths: db/cable_migrate
43 |
--------------------------------------------------------------------------------
/examples/railsapp/config/puma.rb:
--------------------------------------------------------------------------------
1 | # This configuration file will be evaluated by Puma. The top-level methods that
2 | # are invoked here are part of Puma's configuration DSL. For more information
3 | # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html.
4 | #
5 | # Puma starts a configurable number of processes (workers) and each process
6 | # serves each request in a thread from an internal thread pool.
7 | #
8 | # You can control the number of workers using ENV["WEB_CONCURRENCY"]. You
9 | # should only set this value when you want to run 2 or more workers. The
10 | # default is already 1.
11 | #
12 | # The ideal number of threads per worker depends both on how much time the
13 | # application spends waiting for IO operations and on how much you wish to
14 | # prioritize throughput over latency.
15 | #
16 | # As a rule of thumb, increasing the number of threads will increase how much
17 | # traffic a given process can handle (throughput), but due to CRuby's
18 | # Global VM Lock (GVL) it has diminishing returns and will degrade the
19 | # response time (latency) of the application.
20 | #
21 | # The default is set to 3 threads as it's deemed a decent compromise between
22 | # throughput and latency for the average Rails application.
23 | #
24 | # Any libraries that use a connection pool or another resource pool should
25 | # be configured to provide at least as many connections as the number of
26 | # threads. This includes Active Record's `pool` parameter in `database.yml`.
27 | threads_count = ENV.fetch("RAILS_MAX_THREADS", 3)
28 | threads threads_count, threads_count
29 |
30 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
31 | port ENV.fetch("PORT", 3000)
32 |
33 | # Allow puma to be restarted by `bin/rails restart` command.
34 | plugin :tmp_restart
35 |
36 | # Run the Solid Queue supervisor inside of Puma for single-server deployments
37 | plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"]
38 |
39 | # Specify the PID file. Defaults to tmp/pids/server.pid in development.
40 | # In other environments, only set the PID file if requested.
41 | pidfile ENV["PIDFILE"] if ENV["PIDFILE"]
42 |
--------------------------------------------------------------------------------
/examples/railsapp/app/controllers/posts_controller.rb:
--------------------------------------------------------------------------------
1 | class PostsController < ApplicationController
2 | before_action :set_post, only: %i[ show edit update destroy ]
3 |
4 | # GET /posts or /posts.json
5 | def index
6 | @posts = Post.all
7 | end
8 |
9 | # GET /posts/1 or /posts/1.json
10 | def show
11 | end
12 |
13 | # GET /posts/new
14 | def new
15 | @post = Post.new
16 | end
17 |
18 | # GET /posts/1/edit
19 | def edit
20 | end
21 |
22 | # POST /posts or /posts.json
23 | def create
24 | @post = Post.new(post_params)
25 |
26 | respond_to do |format|
27 | if @post.save
28 | format.html { redirect_to @post, notice: "Post was successfully created." }
29 | format.json { render :show, status: :created, location: @post }
30 | else
31 | format.html { render :new, status: :unprocessable_entity }
32 | format.json { render json: @post.errors, status: :unprocessable_entity }
33 | end
34 | end
35 | end
36 |
37 | # PATCH/PUT /posts/1 or /posts/1.json
38 | def update
39 | respond_to do |format|
40 | if @post.update(post_params)
41 | format.html { redirect_to @post, notice: "Post was successfully updated." }
42 | format.json { render :show, status: :ok, location: @post }
43 | else
44 | format.html { render :edit, status: :unprocessable_entity }
45 | format.json { render json: @post.errors, status: :unprocessable_entity }
46 | end
47 | end
48 | end
49 |
50 | # DELETE /posts/1 or /posts/1.json
51 | def destroy
52 | @post.destroy!
53 |
54 | respond_to do |format|
55 | format.html { redirect_to posts_path, status: :see_other, notice: "Post was successfully destroyed." }
56 | format.json { head :no_content }
57 | end
58 | end
59 |
60 | private
61 | # Use callbacks to share common setup or constraints between actions.
62 | def set_post
63 | @post = Post.find(params.expect(:id))
64 | end
65 |
66 | # Only allow a list of trusted parameters through.
67 | def post_params
68 | params.expect(post: [ :name, :title, :content ])
69 | end
70 | end
71 |
--------------------------------------------------------------------------------
/examples/railsapp/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | # The test environment is used exclusively to run your application's
2 | # test suite. You never need to work with it otherwise. Remember that
3 | # your test database is "scratch space" for the test suite and is wiped
4 | # and recreated between test runs. Don't rely on the data there!
5 |
6 | Rails.application.configure do
7 | # Settings specified here will take precedence over those in config/application.rb.
8 |
9 | # While tests run files are not watched, reloading is not necessary.
10 | config.enable_reloading = false
11 |
12 | # Eager loading loads your entire application. When running a single test locally,
13 | # this is usually not necessary, and can slow down your test suite. However, it's
14 | # recommended that you enable it in continuous integration systems to ensure eager
15 | # loading is working properly before deploying your code.
16 | config.eager_load = ENV["CI"].present?
17 |
18 | # Configure public file server for tests with cache-control for performance.
19 | config.public_file_server.headers = { "cache-control" => "public, max-age=3600" }
20 |
21 | # Show full error reports.
22 | config.consider_all_requests_local = true
23 | config.cache_store = :null_store
24 |
25 | # Render exception templates for rescuable exceptions and raise for other exceptions.
26 | config.action_dispatch.show_exceptions = :rescuable
27 |
28 | # Disable request forgery protection in test environment.
29 | config.action_controller.allow_forgery_protection = false
30 |
31 | # Store uploaded files on the local file system in a temporary directory.
32 | config.active_storage.service = :test
33 |
34 | # Tell Action Mailer not to deliver emails to the real world.
35 | # The :test delivery method accumulates sent emails in the
36 | # ActionMailer::Base.deliveries array.
37 | config.action_mailer.delivery_method = :test
38 |
39 | # Set host to be used by links generated in mailer templates.
40 | config.action_mailer.default_url_options = { host: "example.com" }
41 |
42 | # Print deprecation notices to the stderr.
43 | config.active_support.deprecation = :stderr
44 |
45 | # Raises error for missing translations.
46 | # config.i18n.raise_on_missing_translations = true
47 |
48 | # Annotate rendered view with file names.
49 | # config.action_view.annotate_rendered_view_with_filenames = true
50 |
51 | # Raise error when a before_action's only/except options reference missing actions.
52 | config.action_controller.raise_on_missing_callback_actions = true
53 | end
54 |
--------------------------------------------------------------------------------
/examples/railsapp/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches: [ main ]
7 |
8 | jobs:
9 | scan_ruby:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout code
14 | uses: actions/checkout@v4
15 |
16 | - name: Set up Ruby
17 | uses: ruby/setup-ruby@v1
18 | with:
19 | ruby-version: .ruby-version
20 | bundler-cache: true
21 |
22 | - name: Scan for common Rails security vulnerabilities using static analysis
23 | run: bin/brakeman --no-pager
24 |
25 | scan_js:
26 | runs-on: ubuntu-latest
27 |
28 | steps:
29 | - name: Checkout code
30 | uses: actions/checkout@v4
31 |
32 | - name: Set up Ruby
33 | uses: ruby/setup-ruby@v1
34 | with:
35 | ruby-version: .ruby-version
36 | bundler-cache: true
37 |
38 | - name: Scan for security vulnerabilities in JavaScript dependencies
39 | run: bin/importmap audit
40 |
41 | lint:
42 | runs-on: ubuntu-latest
43 | steps:
44 | - name: Checkout code
45 | uses: actions/checkout@v4
46 |
47 | - name: Set up Ruby
48 | uses: ruby/setup-ruby@v1
49 | with:
50 | ruby-version: .ruby-version
51 | bundler-cache: true
52 |
53 | - name: Lint code for consistent style
54 | run: bin/rubocop -f github
55 |
56 | test:
57 | runs-on: ubuntu-latest
58 |
59 | # services:
60 | # redis:
61 | # image: redis
62 | # ports:
63 | # - 6379:6379
64 | # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
65 | steps:
66 | - name: Install packages
67 | run: sudo apt-get update && sudo apt-get install --no-install-recommends -y build-essential git pkg-config google-chrome-stable
68 |
69 | - name: Checkout code
70 | uses: actions/checkout@v4
71 |
72 | - name: Set up Ruby
73 | uses: ruby/setup-ruby@v1
74 | with:
75 | ruby-version: .ruby-version
76 | bundler-cache: true
77 |
78 | - name: Run tests
79 | env:
80 | RAILS_ENV: test
81 | # REDIS_URL: redis://localhost:6379/0
82 | run: bin/rails db:test:prepare test test:system
83 |
84 | - name: Keep screenshots from failed system tests
85 | uses: actions/upload-artifact@v4
86 | if: failure()
87 | with:
88 | name: screenshots
89 | path: ${{ github.workspace }}/tmp/screenshots
90 | if-no-files-found: ignore
91 |
--------------------------------------------------------------------------------
/examples/railsapp/Dockerfile:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile:1
2 | # check=error=true
3 |
4 | # This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand:
5 | # docker build -t railsapp .
6 | # docker run -d -p 80:80 -e RAILS_MASTER_KEY= --name railsapp railsapp
7 |
8 | # For a containerized dev environment, see Dev Containers: https://guides.rubyonrails.org/getting_started_with_devcontainer.html
9 |
10 | # Make sure RUBY_VERSION matches the Ruby version in .ruby-version
11 | ARG RUBY_VERSION=3.3.6
12 | FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base
13 |
14 | # Rails app lives here
15 | WORKDIR /rails
16 |
17 | # Install base packages
18 | RUN apt-get update -qq && \
19 | apt-get install --no-install-recommends -y curl libjemalloc2 libvips sqlite3 && \
20 | rm -rf /var/lib/apt/lists /var/cache/apt/archives
21 |
22 | # Set production environment
23 | ENV RAILS_ENV="production" \
24 | BUNDLE_DEPLOYMENT="1" \
25 | BUNDLE_PATH="/usr/local/bundle" \
26 | BUNDLE_WITHOUT="development"
27 |
28 | # Throw-away build stage to reduce size of final image
29 | FROM base AS build
30 |
31 | # Install packages needed to build gems
32 | RUN apt-get update -qq && \
33 | apt-get install --no-install-recommends -y build-essential git pkg-config && \
34 | rm -rf /var/lib/apt/lists /var/cache/apt/archives
35 |
36 | # Install application gems
37 | COPY Gemfile Gemfile.lock ./
38 | RUN bundle install && \
39 | rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
40 | bundle exec bootsnap precompile --gemfile
41 |
42 | # Copy application code
43 | COPY . .
44 |
45 | # Precompile bootsnap code for faster boot times
46 | RUN bundle exec bootsnap precompile app/ lib/
47 |
48 | # Precompiling assets for production without requiring secret RAILS_MASTER_KEY
49 | RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
50 |
51 |
52 |
53 |
54 | # Final stage for app image
55 | FROM base
56 |
57 | # Copy built artifacts: gems, application
58 | COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
59 | COPY --from=build /rails /rails
60 |
61 | # Run and own only the runtime files as a non-root user for security
62 | RUN groupadd --system --gid 1000 rails && \
63 | useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
64 | chown -R rails:rails db log storage tmp
65 | USER 1000:1000
66 |
67 | # Entrypoint prepares the database.
68 | ENTRYPOINT ["/rails/bin/docker-entrypoint"]
69 |
70 | # Start server via Thruster by default, this can be overwritten at runtime
71 | EXPOSE 80
72 | CMD ["./bin/thrust", "./bin/rails", "server"]
73 |
--------------------------------------------------------------------------------
/examples/railsapp/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
4 | gem "rails", "~> 8.0.1"
5 | # The modern asset pipeline for Rails [https://github.com/rails/propshaft]
6 | gem "propshaft"
7 | # Use sqlite3 as the database for Active Record
8 | gem "sqlite3", ">= 2.1"
9 | # Use the Puma web server [https://github.com/puma/puma]
10 | gem "puma", ">= 5.0"
11 | # Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
12 | gem "importmap-rails"
13 | # Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
14 | gem "turbo-rails"
15 | # Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
16 | gem "stimulus-rails"
17 | # Build JSON APIs with ease [https://github.com/rails/jbuilder]
18 | gem "jbuilder"
19 |
20 | gem "libsql_activerecord", path: "../../"
21 |
22 | # Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
23 | # gem "bcrypt", "~> 3.1.7"
24 |
25 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
26 | gem "tzinfo-data", platforms: %i[ windows jruby ]
27 |
28 | # Use the database-backed adapters for Rails.cache, Active Job, and Action Cable
29 | gem "solid_cache"
30 | gem "solid_queue"
31 | gem "solid_cable"
32 |
33 | # Reduces boot times through caching; required in config/boot.rb
34 | gem "bootsnap", require: false
35 |
36 | # Deploy this application anywhere as a Docker container [https://kamal-deploy.org]
37 | gem "kamal", require: false
38 |
39 | # Add HTTP asset caching/compression and X-Sendfile acceleration to Puma [https://github.com/basecamp/thruster/]
40 | gem "thruster", require: false
41 |
42 | # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
43 | # gem "image_processing", "~> 1.2"
44 |
45 | group :development, :test do
46 | # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
47 | gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
48 |
49 | # Static analysis for security vulnerabilities [https://brakemanscanner.org/]
50 | gem "brakeman", require: false
51 |
52 | # Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/]
53 | gem "rubocop-rails-omakase", require: false
54 | end
55 |
56 | group :development do
57 | # Use console on exceptions pages [https://github.com/rails/web-console]
58 | gem "web-console"
59 | end
60 |
61 | group :test do
62 | # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
63 | gem "capybara"
64 | gem "selenium-webdriver"
65 | end
66 |
--------------------------------------------------------------------------------
/examples/railsapp/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | require "active_support/core_ext/integer/time"
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # Make code changes take effect immediately without server restart.
7 | config.enable_reloading = true
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports.
13 | config.consider_all_requests_local = true
14 |
15 | # Enable server timing.
16 | config.server_timing = true
17 |
18 | # Enable/disable Action Controller caching. By default Action Controller caching is disabled.
19 | # Run rails dev:cache to toggle Action Controller caching.
20 | if Rails.root.join("tmp/caching-dev.txt").exist?
21 | config.action_controller.perform_caching = true
22 | config.action_controller.enable_fragment_cache_logging = true
23 | config.public_file_server.headers = { "cache-control" => "public, max-age=#{2.days.to_i}" }
24 | else
25 | config.action_controller.perform_caching = false
26 | end
27 |
28 | # Change to :null_store to avoid any caching.
29 | config.cache_store = :memory_store
30 |
31 | # Store uploaded files on the local file system (see config/storage.yml for options).
32 | config.active_storage.service = :local
33 |
34 | # Don't care if the mailer can't send.
35 | config.action_mailer.raise_delivery_errors = false
36 |
37 | # Make template changes take effect immediately.
38 | config.action_mailer.perform_caching = false
39 |
40 | # Set localhost to be used by links generated in mailer templates.
41 | config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
42 |
43 | # Print deprecation notices to the Rails logger.
44 | config.active_support.deprecation = :log
45 |
46 | # Raise an error on page load if there are pending migrations.
47 | config.active_record.migration_error = :page_load
48 |
49 | # Highlight code that triggered database queries in logs.
50 | config.active_record.verbose_query_logs = true
51 |
52 | # Append comments with runtime information tags to SQL queries in logs.
53 | config.active_record.query_log_tags_enabled = true
54 |
55 | # Highlight code that enqueued background job in logs.
56 | config.active_job.verbose_enqueue_logs = true
57 |
58 | # Raises error for missing translations.
59 | # config.i18n.raise_on_missing_translations = true
60 |
61 | # Annotate rendered view with file names.
62 | config.action_view.annotate_rendered_view_with_filenames = true
63 |
64 | # Uncomment if you wish to allow Action Cable access from any origin.
65 | # config.action_cable.disable_request_forgery_protection = true
66 |
67 | # Raise error when a before_action's only/except options reference missing actions.
68 | config.action_controller.raise_on_missing_callback_actions = true
69 |
70 | # Apply autocorrection by RuboCop to files generated by `bin/rails generate`.
71 | # config.generators.apply_rubocop_autocorrect_after_generate!
72 | end
73 |
--------------------------------------------------------------------------------
/examples/railsapp/.kamal/hooks/pre-deploy.sample:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # A sample pre-deploy hook
4 | #
5 | # Checks the Github status of the build, waiting for a pending build to complete for up to 720 seconds.
6 | #
7 | # Fails unless the combined status is "success"
8 | #
9 | # These environment variables are available:
10 | # KAMAL_RECORDED_AT
11 | # KAMAL_PERFORMER
12 | # KAMAL_VERSION
13 | # KAMAL_HOSTS
14 | # KAMAL_COMMAND
15 | # KAMAL_SUBCOMMAND
16 | # KAMAL_ROLE (if set)
17 | # KAMAL_DESTINATION (if set)
18 |
19 | # Only check the build status for production deployments
20 | if ENV["KAMAL_COMMAND"] == "rollback" || ENV["KAMAL_DESTINATION"] != "production"
21 | exit 0
22 | end
23 |
24 | require "bundler/inline"
25 |
26 | # true = install gems so this is fast on repeat invocations
27 | gemfile(true, quiet: true) do
28 | source "https://rubygems.org"
29 |
30 | gem "octokit"
31 | gem "faraday-retry"
32 | end
33 |
34 | MAX_ATTEMPTS = 72
35 | ATTEMPTS_GAP = 10
36 |
37 | def exit_with_error(message)
38 | $stderr.puts message
39 | exit 1
40 | end
41 |
42 | class GithubStatusChecks
43 | attr_reader :remote_url, :git_sha, :github_client, :combined_status
44 |
45 | def initialize
46 | @remote_url = `git config --get remote.origin.url`.strip.delete_prefix("https://github.com/")
47 | @git_sha = `git rev-parse HEAD`.strip
48 | @github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
49 | refresh!
50 | end
51 |
52 | def refresh!
53 | @combined_status = github_client.combined_status(remote_url, git_sha)
54 | end
55 |
56 | def state
57 | combined_status[:state]
58 | end
59 |
60 | def first_status_url
61 | first_status = combined_status[:statuses].find { |status| status[:state] == state }
62 | first_status && first_status[:target_url]
63 | end
64 |
65 | def complete_count
66 | combined_status[:statuses].count { |status| status[:state] != "pending"}
67 | end
68 |
69 | def total_count
70 | combined_status[:statuses].count
71 | end
72 |
73 | def current_status
74 | if total_count > 0
75 | "Completed #{complete_count}/#{total_count} checks, see #{first_status_url} ..."
76 | else
77 | "Build not started..."
78 | end
79 | end
80 | end
81 |
82 |
83 | $stdout.sync = true
84 |
85 | puts "Checking build status..."
86 | attempts = 0
87 | checks = GithubStatusChecks.new
88 |
89 | begin
90 | loop do
91 | case checks.state
92 | when "success"
93 | puts "Checks passed, see #{checks.first_status_url}"
94 | exit 0
95 | when "failure"
96 | exit_with_error "Checks failed, see #{checks.first_status_url}"
97 | when "pending"
98 | attempts += 1
99 | end
100 |
101 | exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS
102 |
103 | puts checks.current_status
104 | sleep(ATTEMPTS_GAP)
105 | checks.refresh!
106 | end
107 | rescue Octokit::NotFound
108 | exit_with_error "Build status could not be found"
109 | end
110 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
libSQL ActiveRecord
8 |
9 |
10 |
11 | Databases for all your Ruby on Rails Apps.
12 |
13 |
14 |
15 | Turso ·
16 | Docs ·
17 | Blog & Tutorials
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | ## Features
44 |
45 | - 🚆 Works with [Ruby on Rails](https://rubyonrails.org)
46 | - 🔌 Works offline with [Embedded Replicas](https://docs.turso.tech/features/embedded-replicas/introduction)
47 | - 🌎 Works with remote Turso databases
48 | - ✨ Works with Turso [AI & Vector Search](https://docs.turso.tech/features/ai-and-embeddings)
49 |
50 | > [!WARNING]
51 | > This SDK is currently in technical preview. Join us in Discord to report any issues.
52 |
53 | ## Install
54 |
55 | ```bash
56 | gem install libsql_activerecord
57 | ```
58 |
59 | ## Quickstart
60 |
61 | The example below uses Ruby on Rails with ActiveRecord:
62 |
63 | ```yml
64 | default: &default
65 | adapter: libsql
66 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
67 |
68 | development:
69 | <<: *default
70 | url: <%= ENV['TURSO_DATABASE_URL'] %>
71 | auth_token: <%= ENV['TURSO_AUTH_TOKEN'] %>
72 | path: "path/to/local/replica.db"
73 | ```
74 |
75 | ## Documentation
76 |
77 | Visit our [official documentation](https://docs.turso.tech).
78 |
79 | ## Support
80 |
81 | Join us [on Discord](https://tur.so/discord-activerecord) to get help using this SDK. Report security issues [via email](mailto:security@turso.tech).
82 |
83 | ## Contributors
84 |
85 | See the [contributing guide](CONTRIBUTING.md) to learn how to get involved.
86 |
87 | 
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/examples/railsapp/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'bundle' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "rubygems"
12 |
13 | m = Module.new do
14 | module_function
15 |
16 | def invoked_as_script?
17 | File.expand_path($0) == File.expand_path(__FILE__)
18 | end
19 |
20 | def env_var_version
21 | ENV["BUNDLER_VERSION"]
22 | end
23 |
24 | def cli_arg_version
25 | return unless invoked_as_script? # don't want to hijack other binstubs
26 | return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
27 | bundler_version = nil
28 | update_index = nil
29 | ARGV.each_with_index do |a, i|
30 | if update_index && update_index.succ == i && a.match?(Gem::Version::ANCHORED_VERSION_PATTERN)
31 | bundler_version = a
32 | end
33 | next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
34 | bundler_version = $1
35 | update_index = i
36 | end
37 | bundler_version
38 | end
39 |
40 | def gemfile
41 | gemfile = ENV["BUNDLE_GEMFILE"]
42 | return gemfile if gemfile && !gemfile.empty?
43 |
44 | File.expand_path("../Gemfile", __dir__)
45 | end
46 |
47 | def lockfile
48 | lockfile =
49 | case File.basename(gemfile)
50 | when "gems.rb" then gemfile.sub(/\.rb$/, ".locked")
51 | else "#{gemfile}.lock"
52 | end
53 | File.expand_path(lockfile)
54 | end
55 |
56 | def lockfile_version
57 | return unless File.file?(lockfile)
58 | lockfile_contents = File.read(lockfile)
59 | return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
60 | Regexp.last_match(1)
61 | end
62 |
63 | def bundler_requirement
64 | @bundler_requirement ||=
65 | env_var_version ||
66 | cli_arg_version ||
67 | bundler_requirement_for(lockfile_version)
68 | end
69 |
70 | def bundler_requirement_for(version)
71 | return "#{Gem::Requirement.default}.a" unless version
72 |
73 | bundler_gem_version = Gem::Version.new(version)
74 |
75 | bundler_gem_version.approximate_recommendation
76 | end
77 |
78 | def load_bundler!
79 | ENV["BUNDLE_GEMFILE"] ||= gemfile
80 |
81 | activate_bundler
82 | end
83 |
84 | def activate_bundler
85 | gem_error = activation_error_handling do
86 | gem "bundler", bundler_requirement
87 | end
88 | return if gem_error.nil?
89 | require_error = activation_error_handling do
90 | require "bundler/version"
91 | end
92 | return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
93 | warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
94 | exit 42
95 | end
96 |
97 | def activation_error_handling
98 | yield
99 | nil
100 | rescue StandardError, LoadError => e
101 | e
102 | end
103 | end
104 |
105 | m.load_bundler!
106 |
107 | if m.invoked_as_script?
108 | load Gem.bin_path("bundler", "bundle")
109 | end
110 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "bundix": {
4 | "inputs": {
5 | "nixpkgs": [
6 | "nixpkgs"
7 | ]
8 | },
9 | "locked": {
10 | "lastModified": 1721093334,
11 | "narHash": "sha256-5FghZ0HIETbc0TGcBV8uyixq5z4w/9PF5Puhujz3D1o=",
12 | "owner": "inscapist",
13 | "repo": "bundix",
14 | "rev": "42c08846f7e5d91ef121239fd364feaeb22c0bbc",
15 | "type": "github"
16 | },
17 | "original": {
18 | "owner": "inscapist",
19 | "ref": "main",
20 | "repo": "bundix",
21 | "type": "github"
22 | }
23 | },
24 | "flake-utils": {
25 | "inputs": {
26 | "systems": "systems"
27 | },
28 | "locked": {
29 | "lastModified": 1726560853,
30 | "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
31 | "owner": "numtide",
32 | "repo": "flake-utils",
33 | "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
34 | "type": "github"
35 | },
36 | "original": {
37 | "owner": "numtide",
38 | "repo": "flake-utils",
39 | "type": "github"
40 | }
41 | },
42 | "nixpkgs": {
43 | "locked": {
44 | "lastModified": 1730200266,
45 | "narHash": "sha256-l253w0XMT8nWHGXuXqyiIC/bMvh1VRszGXgdpQlfhvU=",
46 | "owner": "nixos",
47 | "repo": "nixpkgs",
48 | "rev": "807e9154dcb16384b1b765ebe9cd2bba2ac287fd",
49 | "type": "github"
50 | },
51 | "original": {
52 | "owner": "nixos",
53 | "ref": "nixos-unstable",
54 | "repo": "nixpkgs",
55 | "type": "github"
56 | }
57 | },
58 | "nixpkgs_2": {
59 | "locked": {
60 | "lastModified": 1678875422,
61 | "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=",
62 | "owner": "NixOS",
63 | "repo": "nixpkgs",
64 | "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459",
65 | "type": "github"
66 | },
67 | "original": {
68 | "id": "nixpkgs",
69 | "type": "indirect"
70 | }
71 | },
72 | "root": {
73 | "inputs": {
74 | "bundix": "bundix",
75 | "flake-utils": "flake-utils",
76 | "nixpkgs": "nixpkgs",
77 | "ruby-nix": "ruby-nix"
78 | }
79 | },
80 | "ruby-nix": {
81 | "inputs": {
82 | "nixpkgs": "nixpkgs_2"
83 | },
84 | "locked": {
85 | "lastModified": 1725677741,
86 | "narHash": "sha256-CySXGzqycagfrWdcTwzM3Zo1MMm9uUtePYER9BvrW8s=",
87 | "owner": "inscapist",
88 | "repo": "ruby-nix",
89 | "rev": "1f3756f8a713171bf891b39c0d3b1fe6d83a4a63",
90 | "type": "github"
91 | },
92 | "original": {
93 | "owner": "inscapist",
94 | "repo": "ruby-nix",
95 | "type": "github"
96 | }
97 | },
98 | "systems": {
99 | "locked": {
100 | "lastModified": 1681028828,
101 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
102 | "owner": "nix-systems",
103 | "repo": "default",
104 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
105 | "type": "github"
106 | },
107 | "original": {
108 | "owner": "nix-systems",
109 | "repo": "default",
110 | "type": "github"
111 | }
112 | }
113 | },
114 | "root": "root",
115 | "version": 7
116 | }
117 |
--------------------------------------------------------------------------------
/examples/railsapp/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | require "active_support/core_ext/integer/time"
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # Code is not reloaded between requests.
7 | config.enable_reloading = false
8 |
9 | # Eager load code on boot for better performance and memory savings (ignored by Rake tasks).
10 | config.eager_load = true
11 |
12 | # Full error reports are disabled.
13 | config.consider_all_requests_local = false
14 |
15 | # Turn on fragment caching in view templates.
16 | config.action_controller.perform_caching = true
17 |
18 | # Cache assets for far-future expiry since they are all digest stamped.
19 | config.public_file_server.headers = { "cache-control" => "public, max-age=#{1.year.to_i}" }
20 |
21 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
22 | # config.asset_host = "http://assets.example.com"
23 |
24 | # Store uploaded files on the local file system (see config/storage.yml for options).
25 | config.active_storage.service = :local
26 |
27 | # Assume all access to the app is happening through a SSL-terminating reverse proxy.
28 | config.assume_ssl = true
29 |
30 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
31 | config.force_ssl = true
32 |
33 | # Skip http-to-https redirect for the default health check endpoint.
34 | # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } }
35 |
36 | # Log to STDOUT with the current request id as a default log tag.
37 | config.log_tags = [ :request_id ]
38 | config.logger = ActiveSupport::TaggedLogging.logger(STDOUT)
39 |
40 | # Change to "debug" to log everything (including potentially personally-identifiable information!)
41 | config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info")
42 |
43 | # Prevent health checks from clogging up the logs.
44 | config.silence_healthcheck_path = "/up"
45 |
46 | # Don't log any deprecations.
47 | config.active_support.report_deprecations = false
48 |
49 | # Replace the default in-process memory cache store with a durable alternative.
50 | config.cache_store = :solid_cache_store
51 |
52 | # Replace the default in-process and non-durable queuing backend for Active Job.
53 | config.active_job.queue_adapter = :solid_queue
54 | config.solid_queue.connects_to = { database: { writing: :queue } }
55 |
56 | # Ignore bad email addresses and do not raise email delivery errors.
57 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
58 | # config.action_mailer.raise_delivery_errors = false
59 |
60 | # Set host to be used by links generated in mailer templates.
61 | config.action_mailer.default_url_options = { host: "example.com" }
62 |
63 | # Specify outgoing SMTP server. Remember to add smtp/* credentials via rails credentials:edit.
64 | # config.action_mailer.smtp_settings = {
65 | # user_name: Rails.application.credentials.dig(:smtp, :user_name),
66 | # password: Rails.application.credentials.dig(:smtp, :password),
67 | # address: "smtp.example.com",
68 | # port: 587,
69 | # authentication: :plain
70 | # }
71 |
72 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
73 | # the I18n.default_locale when a translation cannot be found).
74 | config.i18n.fallbacks = true
75 |
76 | # Do not dump schema after migrations.
77 | config.active_record.dump_schema_after_migration = false
78 |
79 | # Only use :id for inspections in production.
80 | config.active_record.attributes_for_inspect = [ :id ]
81 |
82 | # Enable DNS rebinding protection and other `Host` header attacks.
83 | # config.hosts = [
84 | # "example.com", # Allow requests from example.com
85 | # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
86 | # ]
87 | #
88 | # Skip DNS rebinding protection for the default health check endpoint.
89 | # config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
90 | end
91 |
--------------------------------------------------------------------------------
/examples/railsapp/config/deploy.yml:
--------------------------------------------------------------------------------
1 | # Name of your application. Used to uniquely configure containers.
2 | service: railsapp
3 |
4 | # Name of the container image.
5 | image: your-user/railsapp
6 |
7 | # Deploy to these servers.
8 | servers:
9 | web:
10 | - 192.168.0.1
11 | # job:
12 | # hosts:
13 | # - 192.168.0.1
14 | # cmd: bin/jobs
15 |
16 | # Enable SSL auto certification via Let's Encrypt and allow for multiple apps on a single web server.
17 | # Remove this section when using multiple web servers and ensure you terminate SSL at your load balancer.
18 | #
19 | # Note: If using Cloudflare, set encryption mode in SSL/TLS setting to "Full" to enable CF-to-app encryption.
20 | proxy:
21 | ssl: true
22 | host: app.example.com
23 |
24 | # Credentials for your image host.
25 | registry:
26 | # Specify the registry server, if you're not using Docker Hub
27 | # server: registry.digitalocean.com / ghcr.io / ...
28 | username: your-user
29 |
30 | # Always use an access token rather than real password when possible.
31 | password:
32 | - KAMAL_REGISTRY_PASSWORD
33 |
34 | # Inject ENV variables into containers (secrets come from .kamal/secrets).
35 | env:
36 | secret:
37 | - RAILS_MASTER_KEY
38 | clear:
39 | # Run the Solid Queue Supervisor inside the web server's Puma process to do jobs.
40 | # When you start using multiple servers, you should split out job processing to a dedicated machine.
41 | SOLID_QUEUE_IN_PUMA: true
42 |
43 | # Set number of processes dedicated to Solid Queue (default: 1)
44 | # JOB_CONCURRENCY: 3
45 |
46 | # Set number of cores available to the application on each server (default: 1).
47 | # WEB_CONCURRENCY: 2
48 |
49 | # Match this to any external database server to configure Active Record correctly
50 | # Use railsapp-db for a db accessory server on same machine via local kamal docker network.
51 | # DB_HOST: 192.168.0.2
52 |
53 | # Log everything from Rails
54 | # RAILS_LOG_LEVEL: debug
55 |
56 | # Aliases are triggered with "bin/kamal ". You can overwrite arguments on invocation:
57 | # "bin/kamal logs -r job" will tail logs from the first server in the job section.
58 | aliases:
59 | console: app exec --interactive --reuse "bin/rails console"
60 | shell: app exec --interactive --reuse "bash"
61 | logs: app logs -f
62 | dbc: app exec --interactive --reuse "bin/rails dbconsole"
63 |
64 |
65 | # Use a persistent storage volume for sqlite database files and local Active Storage files.
66 | # Recommended to change this to a mounted volume path that is backed up off server.
67 | volumes:
68 | - "railsapp_storage:/rails/storage"
69 |
70 |
71 | # Bridge fingerprinted assets, like JS and CSS, between versions to avoid
72 | # hitting 404 on in-flight requests. Combines all files from new and old
73 | # version inside the asset_path.
74 | asset_path: /rails/public/assets
75 |
76 | # Configure the image builder.
77 | builder:
78 | arch: amd64
79 |
80 | # # Build image via remote server (useful for faster amd64 builds on arm64 computers)
81 | # remote: ssh://docker@docker-builder-server
82 | #
83 | # # Pass arguments and secrets to the Docker build process
84 | # args:
85 | # RUBY_VERSION: ruby-3.3.6
86 | # secrets:
87 | # - GITHUB_TOKEN
88 | # - RAILS_MASTER_KEY
89 |
90 | # Use a different ssh user than root
91 | # ssh:
92 | # user: app
93 |
94 | # Use accessory services (secrets come from .kamal/secrets).
95 | # accessories:
96 | # db:
97 | # image: mysql:8.0
98 | # host: 192.168.0.2
99 | # # Change to 3306 to expose port to the world instead of just local network.
100 | # port: "127.0.0.1:3306:3306"
101 | # env:
102 | # clear:
103 | # MYSQL_ROOT_HOST: '%'
104 | # secret:
105 | # - MYSQL_ROOT_PASSWORD
106 | # files:
107 | # - config/mysql/production.cnf:/etc/mysql/my.cnf
108 | # - db/production.sql:/docker-entrypoint-initdb.d/setup.sql
109 | # directories:
110 | # - data:/var/lib/mysql
111 | # redis:
112 | # image: redis:7.0
113 | # host: 192.168.0.2
114 | # port: 6379
115 | # directories:
116 | # - data:/data
117 |
--------------------------------------------------------------------------------
/examples/railsapp/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | The page you were looking for doesn’t exist (404 Not found)
8 |
9 |
10 |
11 |
12 |
13 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
107 |
108 | The page you were looking for doesn’t exist. You may have mistyped the address or the page may have moved. If you’re the application owner check the logs for more information.
109 |
110 |
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/examples/railsapp/db/queue_schema.rb:
--------------------------------------------------------------------------------
1 | ActiveRecord::Schema[7.1].define(version: 1) do
2 | create_table "solid_queue_blocked_executions", force: :cascade do |t|
3 | t.bigint "job_id", null: false
4 | t.string "queue_name", null: false
5 | t.integer "priority", default: 0, null: false
6 | t.string "concurrency_key", null: false
7 | t.datetime "expires_at", null: false
8 | t.datetime "created_at", null: false
9 | t.index [ "concurrency_key", "priority", "job_id" ], name: "index_solid_queue_blocked_executions_for_release"
10 | t.index [ "expires_at", "concurrency_key" ], name: "index_solid_queue_blocked_executions_for_maintenance"
11 | t.index [ "job_id" ], name: "index_solid_queue_blocked_executions_on_job_id", unique: true
12 | end
13 |
14 | create_table "solid_queue_claimed_executions", force: :cascade do |t|
15 | t.bigint "job_id", null: false
16 | t.bigint "process_id"
17 | t.datetime "created_at", null: false
18 | t.index [ "job_id" ], name: "index_solid_queue_claimed_executions_on_job_id", unique: true
19 | t.index [ "process_id", "job_id" ], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id"
20 | end
21 |
22 | create_table "solid_queue_failed_executions", force: :cascade do |t|
23 | t.bigint "job_id", null: false
24 | t.text "error"
25 | t.datetime "created_at", null: false
26 | t.index [ "job_id" ], name: "index_solid_queue_failed_executions_on_job_id", unique: true
27 | end
28 |
29 | create_table "solid_queue_jobs", force: :cascade do |t|
30 | t.string "queue_name", null: false
31 | t.string "class_name", null: false
32 | t.text "arguments"
33 | t.integer "priority", default: 0, null: false
34 | t.string "active_job_id"
35 | t.datetime "scheduled_at"
36 | t.datetime "finished_at"
37 | t.string "concurrency_key"
38 | t.datetime "created_at", null: false
39 | t.datetime "updated_at", null: false
40 | t.index [ "active_job_id" ], name: "index_solid_queue_jobs_on_active_job_id"
41 | t.index [ "class_name" ], name: "index_solid_queue_jobs_on_class_name"
42 | t.index [ "finished_at" ], name: "index_solid_queue_jobs_on_finished_at"
43 | t.index [ "queue_name", "finished_at" ], name: "index_solid_queue_jobs_for_filtering"
44 | t.index [ "scheduled_at", "finished_at" ], name: "index_solid_queue_jobs_for_alerting"
45 | end
46 |
47 | create_table "solid_queue_pauses", force: :cascade do |t|
48 | t.string "queue_name", null: false
49 | t.datetime "created_at", null: false
50 | t.index [ "queue_name" ], name: "index_solid_queue_pauses_on_queue_name", unique: true
51 | end
52 |
53 | create_table "solid_queue_processes", force: :cascade do |t|
54 | t.string "kind", null: false
55 | t.datetime "last_heartbeat_at", null: false
56 | t.bigint "supervisor_id"
57 | t.integer "pid", null: false
58 | t.string "hostname"
59 | t.text "metadata"
60 | t.datetime "created_at", null: false
61 | t.string "name", null: false
62 | t.index [ "last_heartbeat_at" ], name: "index_solid_queue_processes_on_last_heartbeat_at"
63 | t.index [ "name", "supervisor_id" ], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true
64 | t.index [ "supervisor_id" ], name: "index_solid_queue_processes_on_supervisor_id"
65 | end
66 |
67 | create_table "solid_queue_ready_executions", force: :cascade do |t|
68 | t.bigint "job_id", null: false
69 | t.string "queue_name", null: false
70 | t.integer "priority", default: 0, null: false
71 | t.datetime "created_at", null: false
72 | t.index [ "job_id" ], name: "index_solid_queue_ready_executions_on_job_id", unique: true
73 | t.index [ "priority", "job_id" ], name: "index_solid_queue_poll_all"
74 | t.index [ "queue_name", "priority", "job_id" ], name: "index_solid_queue_poll_by_queue"
75 | end
76 |
77 | create_table "solid_queue_recurring_executions", force: :cascade do |t|
78 | t.bigint "job_id", null: false
79 | t.string "task_key", null: false
80 | t.datetime "run_at", null: false
81 | t.datetime "created_at", null: false
82 | t.index [ "job_id" ], name: "index_solid_queue_recurring_executions_on_job_id", unique: true
83 | t.index [ "task_key", "run_at" ], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true
84 | end
85 |
86 | create_table "solid_queue_recurring_tasks", force: :cascade do |t|
87 | t.string "key", null: false
88 | t.string "schedule", null: false
89 | t.string "command", limit: 2048
90 | t.string "class_name"
91 | t.text "arguments"
92 | t.string "queue_name"
93 | t.integer "priority", default: 0
94 | t.boolean "static", default: true, null: false
95 | t.text "description"
96 | t.datetime "created_at", null: false
97 | t.datetime "updated_at", null: false
98 | t.index [ "key" ], name: "index_solid_queue_recurring_tasks_on_key", unique: true
99 | t.index [ "static" ], name: "index_solid_queue_recurring_tasks_on_static"
100 | end
101 |
102 | create_table "solid_queue_scheduled_executions", force: :cascade do |t|
103 | t.bigint "job_id", null: false
104 | t.string "queue_name", null: false
105 | t.integer "priority", default: 0, null: false
106 | t.datetime "scheduled_at", null: false
107 | t.datetime "created_at", null: false
108 | t.index [ "job_id" ], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true
109 | t.index [ "scheduled_at", "priority", "job_id" ], name: "index_solid_queue_dispatch_all"
110 | end
111 |
112 | create_table "solid_queue_semaphores", force: :cascade do |t|
113 | t.string "key", null: false
114 | t.integer "value", default: 1, null: false
115 | t.datetime "expires_at", null: false
116 | t.datetime "created_at", null: false
117 | t.datetime "updated_at", null: false
118 | t.index [ "expires_at" ], name: "index_solid_queue_semaphores_on_expires_at"
119 | t.index [ "key", "value" ], name: "index_solid_queue_semaphores_on_key_and_value"
120 | t.index [ "key" ], name: "index_solid_queue_semaphores_on_key", unique: true
121 | end
122 |
123 | add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
124 | add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
125 | add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
126 | add_foreign_key "solid_queue_ready_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
127 | add_foreign_key "solid_queue_recurring_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
128 | add_foreign_key "solid_queue_scheduled_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
129 | end
130 |
--------------------------------------------------------------------------------
/examples/railsapp/public/400.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | The server cannot process the request due to a client error (400 Bad Request)
8 |
9 |
10 |
11 |
12 |
13 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
107 |
108 | The server cannot process the request due to a client error. Please check the request and try again. If you’re the application owner check the logs for more information.
109 |
110 |
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/gemset.nix:
--------------------------------------------------------------------------------
1 | {
2 | activemodel = {
3 | dependencies = ["activesupport"];
4 | groups = ["default"];
5 | platforms = [];
6 | source = {
7 | remotes = ["https://rubygems.org"];
8 | sha256 = "0y6wc88840pc2js15i244nr9xz9km6c451c8slc8w4ncdpyr4qpl";
9 | target = "ruby";
10 | type = "gem";
11 | };
12 | targets = [];
13 | version = "8.0.1";
14 | };
15 | activerecord = {
16 | dependencies = ["activemodel" "activesupport" "timeout"];
17 | groups = ["default"];
18 | platforms = [];
19 | source = {
20 | remotes = ["https://rubygems.org"];
21 | sha256 = "01jk9dr1y5q911z19f7mr3fyizrmim5ms0k30d7p1fv00rhz19rl";
22 | target = "ruby";
23 | type = "gem";
24 | };
25 | targets = [];
26 | version = "8.0.1";
27 | };
28 | activesupport = {
29 | dependencies = ["base64" "benchmark" "bigdecimal" "concurrent-ruby" "connection_pool" "drb" "i18n" "logger" "minitest" "securerandom" "tzinfo" "uri"];
30 | groups = ["default"];
31 | platforms = [];
32 | source = {
33 | remotes = ["https://rubygems.org"];
34 | sha256 = "0drfj44a16r86clrrqz3vqmg93qri6bqghjm21ac6jn2853cfnzx";
35 | target = "ruby";
36 | type = "gem";
37 | };
38 | targets = [];
39 | version = "8.0.1";
40 | };
41 | base64 = {
42 | groups = ["default"];
43 | platforms = [];
44 | source = {
45 | remotes = ["https://rubygems.org"];
46 | sha256 = "01qml0yilb9basf7is2614skjp8384h2pycfx86cr8023arfj98g";
47 | target = "ruby";
48 | type = "gem";
49 | };
50 | targets = [];
51 | version = "0.2.0";
52 | };
53 | benchmark = {
54 | groups = ["default"];
55 | platforms = [];
56 | source = {
57 | remotes = ["https://rubygems.org"];
58 | sha256 = "0jl71qcgamm96dzyqk695j24qszhcc7liw74qc83fpjljp2gh4hg";
59 | target = "ruby";
60 | type = "gem";
61 | };
62 | targets = [];
63 | version = "0.4.0";
64 | };
65 | bigdecimal = {
66 | groups = ["default"];
67 | platforms = [];
68 | source = {
69 | remotes = ["https://rubygems.org"];
70 | sha256 = "1gi7zqgmqwi5lizggs1jhc3zlwaqayy9rx2ah80sxy24bbnng558";
71 | target = "ruby";
72 | type = "gem";
73 | };
74 | targets = [];
75 | version = "3.1.8";
76 | };
77 | concurrent-ruby = {
78 | groups = ["default"];
79 | platforms = [];
80 | source = {
81 | remotes = ["https://rubygems.org"];
82 | sha256 = "0chwfdq2a6kbj6xz9l6zrdfnyghnh32si82la1dnpa5h75ir5anl";
83 | target = "ruby";
84 | type = "gem";
85 | };
86 | targets = [];
87 | version = "1.3.4";
88 | };
89 | connection_pool = {
90 | groups = ["default"];
91 | platforms = [];
92 | source = {
93 | remotes = ["https://rubygems.org"];
94 | sha256 = "1x32mcpm2cl5492kd6lbjbaf17qsssmpx9kdyr7z1wcif2cwyh0g";
95 | target = "ruby";
96 | type = "gem";
97 | };
98 | targets = [];
99 | version = "2.4.1";
100 | };
101 | drb = {
102 | groups = ["default"];
103 | platforms = [];
104 | source = {
105 | remotes = ["https://rubygems.org"];
106 | sha256 = "0h5kbj9hvg5hb3c7l425zpds0vb42phvln2knab8nmazg2zp5m79";
107 | target = "ruby";
108 | type = "gem";
109 | };
110 | targets = [];
111 | version = "2.2.1";
112 | };
113 | ffi = {
114 | groups = ["default"];
115 | platforms = [];
116 | source = {
117 | remotes = ["https://rubygems.org"];
118 | sha256 = "07139870npj59jnl8vmk39ja3gdk3fb5z9vc0lf32y2h891hwqsi";
119 | target = "ruby";
120 | type = "gem";
121 | };
122 | targets = [{
123 | remotes = ["https://rubygems.org"];
124 | sha256 = "04hdrlzyri00lgwi4rh8vzy8z8x789p5gc45sk344ib1fr78g730";
125 | target = "arm64-darwin";
126 | targetCPU = "arm64";
127 | targetOS = "darwin";
128 | type = "gem";
129 | }];
130 | version = "1.17.0";
131 | };
132 | i18n = {
133 | dependencies = ["concurrent-ruby"];
134 | groups = ["default"];
135 | platforms = [];
136 | source = {
137 | remotes = ["https://rubygems.org"];
138 | sha256 = "0k31wcgnvcvd14snz0pfqj976zv6drfsnq6x8acz10fiyms9l8nw";
139 | target = "ruby";
140 | type = "gem";
141 | };
142 | targets = [];
143 | version = "1.14.6";
144 | };
145 | libsql_activerecord = {
146 | dependencies = ["activerecord" "turso_libsql"];
147 | groups = ["default"];
148 | platforms = [];
149 | source = {
150 | path = ".";
151 | type = "path";
152 | };
153 | targets = [];
154 | version = "0.0.1";
155 | };
156 | logger = {
157 | groups = ["default"];
158 | platforms = [];
159 | source = {
160 | remotes = ["https://rubygems.org"];
161 | sha256 = "1574gi74z5pww36rv0jvqlv9ybm87h7c37fb5r2axn3mbh0wwcs5";
162 | target = "ruby";
163 | type = "gem";
164 | };
165 | targets = [];
166 | version = "1.6.3";
167 | };
168 | minitest = {
169 | groups = ["default"];
170 | platforms = [];
171 | source = {
172 | remotes = ["https://rubygems.org"];
173 | sha256 = "0izrg03wn2yj3gd76ck7ifbm9h2kgy8kpg4fk06ckpy4bbicmwlw";
174 | target = "ruby";
175 | type = "gem";
176 | };
177 | targets = [];
178 | version = "5.25.4";
179 | };
180 | securerandom = {
181 | groups = ["default"];
182 | platforms = [];
183 | source = {
184 | remotes = ["https://rubygems.org"];
185 | sha256 = "1cd0iriqfsf1z91qg271sm88xjnfd92b832z49p1nd542ka96lfc";
186 | target = "ruby";
187 | type = "gem";
188 | };
189 | targets = [];
190 | version = "0.4.1";
191 | };
192 | timeout = {
193 | groups = ["default"];
194 | platforms = [];
195 | source = {
196 | remotes = ["https://rubygems.org"];
197 | sha256 = "03p31w5ghqfsbz5mcjzvwgkw3h9lbvbknqvrdliy8pxmn9wz02cm";
198 | target = "ruby";
199 | type = "gem";
200 | };
201 | targets = [];
202 | version = "0.4.3";
203 | };
204 | turso_libsql = {
205 | dependencies = ["ffi"];
206 | groups = ["default"];
207 | platforms = [];
208 | source = {
209 | remotes = ["https://rubygems.org"];
210 | sha256 = "1cydx9wl1lvzfk8225fb07awksl3h5l7i9d960hn1rgp1p43gj7d";
211 | target = "ruby";
212 | type = "gem";
213 | };
214 | targets = [];
215 | version = "0.2.0";
216 | };
217 | tzinfo = {
218 | dependencies = ["concurrent-ruby"];
219 | groups = ["default"];
220 | platforms = [];
221 | source = {
222 | remotes = ["https://rubygems.org"];
223 | sha256 = "16w2g84dzaf3z13gxyzlzbf748kylk5bdgg3n1ipvkvvqy685bwd";
224 | target = "ruby";
225 | type = "gem";
226 | };
227 | targets = [];
228 | version = "2.0.6";
229 | };
230 | uri = {
231 | groups = ["default"];
232 | platforms = [];
233 | source = {
234 | remotes = ["https://rubygems.org"];
235 | sha256 = "09qyg6a29cfgd46qid8qvx4sjbv596v19ym73xvhanbyxd6500xk";
236 | target = "ruby";
237 | type = "gem";
238 | };
239 | targets = [];
240 | version = "1.0.2";
241 | };
242 | }
--------------------------------------------------------------------------------
/examples/railsapp/public/406-unsupported-browser.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Your browser is not supported (406 Not Acceptable)
8 |
9 |
10 |
11 |
12 |
13 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
107 |
108 | Your browser is not supported. Please upgrade your browser to continue.
109 |
110 |
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/examples/railsapp/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | We’re sorry, but something went wrong (500 Internal Server Error)
8 |
9 |
10 |
11 |
12 |
13 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
107 |
108 | We’re sorry, but something went wrong. If you’re the application owner check the logs for more information.
109 |
110 |
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/examples/railsapp/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | The change you wanted was rejected (422 Unprocessable Entity)
8 |
9 |
10 |
11 |
12 |
13 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
107 |
108 | The change you wanted was rejected. Maybe you tried to change something you didn’t have access to. If you’re the application owner check the logs for more information.
109 |
110 |
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/lib/active_record/connection_adapters/libsql_adapter.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'active_record'
4 | require 'active_record/base'
5 | require 'active_record/connection_adapters/abstract_adapter'
6 | require 'active_record/connection_adapters/sqlite3/database_statements'
7 | require 'active_record/connection_adapters/sqlite3/schema_statements'
8 | require 'active_record/connection_adapters/sqlite3/quoting'
9 |
10 | require 'libsql'
11 |
12 | module ActiveRecord
13 | class Base # :nodoc:
14 | class << self
15 | def libsql_connection(config)
16 | config = config.symbolize_keys
17 | connection = ::Libsql::Database.new config
18 | ConnectionAdapters::LibsqlAdapter.new(connection, logger, config)
19 | end
20 | end
21 | end
22 |
23 | module ConnectionAdapters # :nodoc:
24 | if ActiveRecord.version >= Gem::Version.new('7.2')
25 | register 'libsql', 'ActiveRecord::ConnectionAdapters::LibsqlAdapter',
26 | 'active_record/connection_adapters/libsql_adapter'
27 | end
28 |
29 | class LibsqlAdapter < AbstractAdapter # :nodoc:
30 | ADAPTER_NAME = 'libSQL'
31 |
32 | NATIVE_DATABASE_TYPES = {
33 | primary_key: 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',
34 | string: { name: 'varchar' },
35 | text: { name: 'text' },
36 | integer: { name: 'integer' },
37 | float: { name: 'float' },
38 | decimal: { name: 'decimal' },
39 | datetime: { name: 'datetime' },
40 | time: { name: 'time' },
41 | date: { name: 'date' },
42 | binary: { name: 'blob' },
43 | boolean: { name: 'boolean' },
44 | json: { name: 'json' }
45 | }.freeze
46 |
47 | READ_QUERY = AbstractAdapter.build_read_query_regexp(:pragma)
48 | private_constant :READ_QUERY
49 |
50 | def write_query?(sql) # :nodoc:
51 | !READ_QUERY.match?(sql)
52 | end
53 |
54 | def native_database_types # :nodoc:
55 | NATIVE_DATABASE_TYPES
56 | end
57 |
58 | class << self
59 | def new_client(config)
60 | db = Libsql::Database.new(config || {})
61 | db.connect
62 | end
63 | end
64 |
65 | def initialize(...)
66 | super
67 | @connection_parameters = @config.reject { |k| k == :adapter }
68 | @connection_parameters[:url] = @connection_parameters[:host]
69 | end
70 |
71 | def connect
72 | @raw_connection = self.class.new_client(@connection_parameters)
73 | end
74 |
75 | def reconnect
76 | @raw_connection&.close
77 | connect
78 | end
79 |
80 | def perform_query(
81 | raw_connection, sql, binds, type_casted_binds, prepare:,
82 | notification_payload:, batch: false
83 | )
84 | _ = prepare
85 | _ = notification_payload
86 | _ = binds
87 |
88 | if batch
89 | raw_connection.execute_batch(sql)
90 | else
91 | stmt = raw_connection.prepare(sql)
92 | begin
93 | result =
94 | if stmt.column_count.zero?
95 | @last_affected_rows = stmt.execute type_casted_binds
96 | ActiveRecord::Result.empty
97 | else
98 | rows = stmt.query(type_casted_binds)
99 | @last_affected_rows = nil
100 | ActiveRecord::Result.new(rows.columns, rows.to_a.map(&:values))
101 | end
102 | ensure
103 | stmt.close
104 | end
105 | end
106 | verified!
107 |
108 | result
109 | end
110 |
111 | def affected_rows(_result)
112 | @last_affected_rows
113 | end
114 |
115 | def cast_result(result)
116 | result
117 | end
118 |
119 | def quote_column_name(name)
120 | %("#{name.to_s.gsub('"', '""')}").freeze
121 | end
122 |
123 | def quote_table_name(name)
124 | %("#{name.to_s.gsub('"', '""').gsub('.', '"."')}").freeze
125 | end
126 |
127 | def column_definitions(table_name)
128 | internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", 'SCHEMA')
129 | end
130 |
131 | def data_source_sql(name = nil, type: nil)
132 | scope = quoted_scope(name, type:)
133 | scope[:type] ||= "'table','view'"
134 |
135 | sql = +"SELECT name FROM pragma_table_list WHERE schema <> 'temp'"
136 | sql << " AND name NOT IN ('sqlite_sequence', 'sqlite_schema')"
137 | sql << " AND name = #{scope[:name]}" if scope[:name]
138 | sql << " AND type IN (#{scope[:type]})"
139 | sql
140 | end
141 |
142 | def quoted_scope(name = nil, type: nil)
143 | type = {
144 | 'BASE_TABLE': "'table'",
145 | 'VIEW': "'view'",
146 | 'VIRTUAL TABLE': "'virtual'"
147 | }[type]
148 |
149 | scope = {}
150 | scope[:name] = quote(name) if name
151 | scope[:type] = type if type
152 | scope
153 | end
154 |
155 | def extract_value_from_default(default)
156 | case default
157 | when /^null$/i then nil
158 | when /^'([^|]*)'$/m then::Regexp.last_match(1).gsub("''", "'")
159 | when /^"([^|]*)"$/m then ::Regexp.last_match(1).gsub('""', '"')
160 | when /\A-?\d+(\.\d*)?\z/ then ::Regexp.last_match(0)
161 | when /x'(.*)'/ then [::Regexp.last_match(1)].pack('H*')
162 | end
163 | end
164 |
165 | def extract_default_function(default_value, default)
166 | default if default_function?(default_value, default)
167 | end
168 |
169 | def default_function?(default_value, default)
170 | !default_value && /\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP|\|\|/.match?(default)
171 | end
172 |
173 | def extract_generated_type(field)
174 | case field['hidden']
175 | when 2 then :virtual
176 | when 3 then :stored
177 | end
178 | end
179 |
180 | def column_the_rowid?(field, column_definitions)
181 | return false unless /integer/i.match?(field['type']) && field['pk'] == 1
182 |
183 | column_definitions.one? { |c| c['pk'].positive? }
184 | end
185 |
186 | def new_column_from_field(_table_name, field, definitions)
187 | default = field['dflt_value']
188 |
189 | type_metadata = fetch_type_metadata(field['type'])
190 | default_value = extract_value_from_default(default)
191 | generated_type = extract_generated_type(field)
192 |
193 | default_function =
194 | if generated_type.present?
195 | default
196 | else
197 | extract_default_function(default_value, default)
198 | end
199 |
200 | rowid = column_the_rowid?(field, definitions)
201 |
202 | Column.new(
203 | field['name'],
204 | default_value,
205 | type_metadata,
206 | field['notnull'].to_i.zero?,
207 | default_function,
208 | collation: field['collation'],
209 | auto_increment: field['auto_increment'],
210 | rowid:,
211 | generated_type:
212 | )
213 | end
214 |
215 | def primary_keys(table_name) # :nodoc:
216 | column_definitions(table_name)
217 | .select { |f| f['pk'].positive? }
218 | .sort_by { |f| f['pk'] }
219 | .map { |f| f['name'] }
220 | end
221 |
222 | def last_inserted_id(result)
223 | @raw_connection.last_inserted_id
224 | end
225 |
226 | def indexes(table_name)
227 | internal_exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').filter_map do |row|
228 | # Indexes SQLite creates implicitly for internal use start with "sqlite_".
229 | # See https://www.sqlite.org/fileformat2.html#intschema
230 | next if row['name'].start_with?('sqlite_')
231 |
232 | index_sql = query_value(<<~SQL, 'SCHEMA')
233 | SELECT sql
234 | FROM sqlite_master
235 | WHERE name = #{quote(row['name'])} AND type = 'index'
236 | UNION ALL
237 | SELECT sql
238 | FROM sqlite_temp_master
239 | WHERE name = #{quote(row['name'])} AND type = 'index'
240 | SQL
241 |
242 | %r{\bON\b\s*"?(\w+?)"?\s*\((?.+?)\)(?:\s*WHERE\b\s*(?.+))?(?:\s*/\*.*\*/)?\z}i =~ index_sql
243 |
244 | columns = internal_exec_query("PRAGMA index_info(#{quote(row['name'])})", 'SCHEMA').map do |col|
245 | col['name']
246 | end
247 |
248 | where = where.sub(%r{\s*/\*.*\*/\z}, '') if where
249 | orders = {}
250 |
251 | if columns.any?(&:nil?) # index created with an expression
252 | columns = expressions
253 | elsif index_sql
254 | # Add info on sort order for columns (only desc order is explicitly specified,
255 | # asc is the default)
256 | index_sql.scan(/"(\w+)" DESC/).flatten.each do |order_column|
257 | orders[order_column] = :desc
258 | end # index_sql can be null in case of primary key indexes
259 | end
260 |
261 | IndexDefinition.new(
262 | table_name,
263 | row['name'],
264 | row['unique'] != 0,
265 | columns,
266 | where:,
267 | orders:
268 | )
269 | end
270 | end
271 | end
272 | end
273 | end
274 |
--------------------------------------------------------------------------------
/examples/railsapp/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ../..
3 | specs:
4 | libsql_activerecord (0.0.2)
5 | activerecord (~> 8.0)
6 | turso_libsql (~> 0.2)
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | specs:
11 | actioncable (8.0.1)
12 | actionpack (= 8.0.1)
13 | activesupport (= 8.0.1)
14 | nio4r (~> 2.0)
15 | websocket-driver (>= 0.6.1)
16 | zeitwerk (~> 2.6)
17 | actionmailbox (8.0.1)
18 | actionpack (= 8.0.1)
19 | activejob (= 8.0.1)
20 | activerecord (= 8.0.1)
21 | activestorage (= 8.0.1)
22 | activesupport (= 8.0.1)
23 | mail (>= 2.8.0)
24 | actionmailer (8.0.1)
25 | actionpack (= 8.0.1)
26 | actionview (= 8.0.1)
27 | activejob (= 8.0.1)
28 | activesupport (= 8.0.1)
29 | mail (>= 2.8.0)
30 | rails-dom-testing (~> 2.2)
31 | actionpack (8.0.1)
32 | actionview (= 8.0.1)
33 | activesupport (= 8.0.1)
34 | nokogiri (>= 1.8.5)
35 | rack (>= 2.2.4)
36 | rack-session (>= 1.0.1)
37 | rack-test (>= 0.6.3)
38 | rails-dom-testing (~> 2.2)
39 | rails-html-sanitizer (~> 1.6)
40 | useragent (~> 0.16)
41 | actiontext (8.0.1)
42 | actionpack (= 8.0.1)
43 | activerecord (= 8.0.1)
44 | activestorage (= 8.0.1)
45 | activesupport (= 8.0.1)
46 | globalid (>= 0.6.0)
47 | nokogiri (>= 1.8.5)
48 | actionview (8.0.1)
49 | activesupport (= 8.0.1)
50 | builder (~> 3.1)
51 | erubi (~> 1.11)
52 | rails-dom-testing (~> 2.2)
53 | rails-html-sanitizer (~> 1.6)
54 | activejob (8.0.1)
55 | activesupport (= 8.0.1)
56 | globalid (>= 0.3.6)
57 | activemodel (8.0.1)
58 | activesupport (= 8.0.1)
59 | activerecord (8.0.1)
60 | activemodel (= 8.0.1)
61 | activesupport (= 8.0.1)
62 | timeout (>= 0.4.0)
63 | activestorage (8.0.1)
64 | actionpack (= 8.0.1)
65 | activejob (= 8.0.1)
66 | activerecord (= 8.0.1)
67 | activesupport (= 8.0.1)
68 | marcel (~> 1.0)
69 | activesupport (8.0.1)
70 | base64
71 | benchmark (>= 0.3)
72 | bigdecimal
73 | concurrent-ruby (~> 1.0, >= 1.3.1)
74 | connection_pool (>= 2.2.5)
75 | drb
76 | i18n (>= 1.6, < 2)
77 | logger (>= 1.4.2)
78 | minitest (>= 5.1)
79 | securerandom (>= 0.3)
80 | tzinfo (~> 2.0, >= 2.0.5)
81 | uri (>= 0.13.1)
82 | addressable (2.8.7)
83 | public_suffix (>= 2.0.2, < 7.0)
84 | ast (2.4.2)
85 | base64 (0.2.0)
86 | bcrypt_pbkdf (1.1.1)
87 | bcrypt_pbkdf (1.1.1-arm64-darwin)
88 | bcrypt_pbkdf (1.1.1-x86_64-darwin)
89 | benchmark (0.4.0)
90 | bigdecimal (3.1.8)
91 | bindex (0.8.1)
92 | bootsnap (1.18.4)
93 | msgpack (~> 1.2)
94 | brakeman (6.2.2)
95 | racc
96 | builder (3.3.0)
97 | capybara (3.40.0)
98 | addressable
99 | matrix
100 | mini_mime (>= 0.1.3)
101 | nokogiri (~> 1.11)
102 | rack (>= 1.6.0)
103 | rack-test (>= 0.6.3)
104 | regexp_parser (>= 1.5, < 3.0)
105 | xpath (~> 3.2)
106 | concurrent-ruby (1.3.4)
107 | connection_pool (2.4.1)
108 | crass (1.0.6)
109 | date (3.4.1)
110 | debug (1.9.2)
111 | irb (~> 1.10)
112 | reline (>= 0.3.8)
113 | dotenv (3.1.6)
114 | drb (2.2.1)
115 | ed25519 (1.3.0)
116 | erubi (1.13.0)
117 | et-orbi (1.2.11)
118 | tzinfo
119 | ffi (1.17.0-aarch64-linux-gnu)
120 | ffi (1.17.0-aarch64-linux-musl)
121 | ffi (1.17.0-arm-linux-gnu)
122 | ffi (1.17.0-arm-linux-musl)
123 | ffi (1.17.0-arm64-darwin)
124 | ffi (1.17.0-x86-linux-gnu)
125 | ffi (1.17.0-x86-linux-musl)
126 | ffi (1.17.0-x86_64-darwin)
127 | ffi (1.17.0-x86_64-linux-gnu)
128 | ffi (1.17.0-x86_64-linux-musl)
129 | fugit (1.11.1)
130 | et-orbi (~> 1, >= 1.2.11)
131 | raabro (~> 1.4)
132 | globalid (1.2.1)
133 | activesupport (>= 6.1)
134 | i18n (1.14.6)
135 | concurrent-ruby (~> 1.0)
136 | importmap-rails (2.0.3)
137 | actionpack (>= 6.0.0)
138 | activesupport (>= 6.0.0)
139 | railties (>= 6.0.0)
140 | io-console (0.8.0)
141 | irb (1.14.2)
142 | rdoc (>= 4.0.0)
143 | reline (>= 0.4.2)
144 | jbuilder (2.13.0)
145 | actionview (>= 5.0.0)
146 | activesupport (>= 5.0.0)
147 | json (2.9.0)
148 | kamal (2.4.0)
149 | activesupport (>= 7.0)
150 | base64 (~> 0.2)
151 | bcrypt_pbkdf (~> 1.0)
152 | concurrent-ruby (~> 1.2)
153 | dotenv (~> 3.1)
154 | ed25519 (~> 1.2)
155 | net-ssh (~> 7.3)
156 | sshkit (>= 1.23.0, < 2.0)
157 | thor (~> 1.3)
158 | zeitwerk (>= 2.6.18, < 3.0)
159 | language_server-protocol (3.17.0.3)
160 | logger (1.6.3)
161 | loofah (2.23.1)
162 | crass (~> 1.0.2)
163 | nokogiri (>= 1.12.0)
164 | mail (2.8.1)
165 | mini_mime (>= 0.1.1)
166 | net-imap
167 | net-pop
168 | net-smtp
169 | marcel (1.0.4)
170 | matrix (0.4.2)
171 | mini_mime (1.1.5)
172 | minitest (5.25.4)
173 | msgpack (1.7.5)
174 | net-imap (0.5.1)
175 | date
176 | net-protocol
177 | net-pop (0.1.2)
178 | net-protocol
179 | net-protocol (0.2.2)
180 | timeout
181 | net-scp (4.0.0)
182 | net-ssh (>= 2.6.5, < 8.0.0)
183 | net-sftp (4.0.0)
184 | net-ssh (>= 5.0.0, < 8.0.0)
185 | net-smtp (0.5.0)
186 | net-protocol
187 | net-ssh (7.3.0)
188 | nio4r (2.7.4)
189 | nokogiri (1.17.2-aarch64-linux)
190 | racc (~> 1.4)
191 | nokogiri (1.17.2-arm-linux)
192 | racc (~> 1.4)
193 | nokogiri (1.17.2-arm64-darwin)
194 | racc (~> 1.4)
195 | nokogiri (1.17.2-x86-linux)
196 | racc (~> 1.4)
197 | nokogiri (1.17.2-x86_64-darwin)
198 | racc (~> 1.4)
199 | nokogiri (1.17.2-x86_64-linux)
200 | racc (~> 1.4)
201 | ostruct (0.6.1)
202 | parallel (1.26.3)
203 | parser (3.3.6.0)
204 | ast (~> 2.4.1)
205 | racc
206 | propshaft (1.1.0)
207 | actionpack (>= 7.0.0)
208 | activesupport (>= 7.0.0)
209 | rack
210 | railties (>= 7.0.0)
211 | psych (5.2.1)
212 | date
213 | stringio
214 | public_suffix (6.0.1)
215 | puma (6.5.0)
216 | nio4r (~> 2.0)
217 | raabro (1.4.0)
218 | racc (1.8.1)
219 | rack (3.1.8)
220 | rack-session (2.0.0)
221 | rack (>= 3.0.0)
222 | rack-test (2.1.0)
223 | rack (>= 1.3)
224 | rackup (2.2.1)
225 | rack (>= 3)
226 | rails (8.0.1)
227 | actioncable (= 8.0.1)
228 | actionmailbox (= 8.0.1)
229 | actionmailer (= 8.0.1)
230 | actionpack (= 8.0.1)
231 | actiontext (= 8.0.1)
232 | actionview (= 8.0.1)
233 | activejob (= 8.0.1)
234 | activemodel (= 8.0.1)
235 | activerecord (= 8.0.1)
236 | activestorage (= 8.0.1)
237 | activesupport (= 8.0.1)
238 | bundler (>= 1.15.0)
239 | railties (= 8.0.1)
240 | rails-dom-testing (2.2.0)
241 | activesupport (>= 5.0.0)
242 | minitest
243 | nokogiri (>= 1.6)
244 | rails-html-sanitizer (1.6.2)
245 | loofah (~> 2.21)
246 | nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
247 | railties (8.0.1)
248 | actionpack (= 8.0.1)
249 | activesupport (= 8.0.1)
250 | irb (~> 1.13)
251 | rackup (>= 1.0.0)
252 | rake (>= 12.2)
253 | thor (~> 1.0, >= 1.2.2)
254 | zeitwerk (~> 2.6)
255 | rainbow (3.1.1)
256 | rake (13.2.1)
257 | rdoc (6.9.0)
258 | psych (>= 4.0.0)
259 | regexp_parser (2.9.3)
260 | reline (0.5.12)
261 | io-console (~> 0.5)
262 | rexml (3.4.0)
263 | rubocop (1.69.2)
264 | json (~> 2.3)
265 | language_server-protocol (>= 3.17.0)
266 | parallel (~> 1.10)
267 | parser (>= 3.3.0.2)
268 | rainbow (>= 2.2.2, < 4.0)
269 | regexp_parser (>= 2.9.3, < 3.0)
270 | rubocop-ast (>= 1.36.2, < 2.0)
271 | ruby-progressbar (~> 1.7)
272 | unicode-display_width (>= 2.4.0, < 4.0)
273 | rubocop-ast (1.37.0)
274 | parser (>= 3.3.1.0)
275 | rubocop-minitest (0.36.0)
276 | rubocop (>= 1.61, < 2.0)
277 | rubocop-ast (>= 1.31.1, < 2.0)
278 | rubocop-performance (1.23.0)
279 | rubocop (>= 1.48.1, < 2.0)
280 | rubocop-ast (>= 1.31.1, < 2.0)
281 | rubocop-rails (2.27.0)
282 | activesupport (>= 4.2.0)
283 | rack (>= 1.1)
284 | rubocop (>= 1.52.0, < 2.0)
285 | rubocop-ast (>= 1.31.1, < 2.0)
286 | rubocop-rails-omakase (1.0.0)
287 | rubocop
288 | rubocop-minitest
289 | rubocop-performance
290 | rubocop-rails
291 | ruby-progressbar (1.13.0)
292 | rubyzip (2.3.2)
293 | securerandom (0.4.1)
294 | selenium-webdriver (4.27.0)
295 | base64 (~> 0.2)
296 | logger (~> 1.4)
297 | rexml (~> 3.2, >= 3.2.5)
298 | rubyzip (>= 1.2.2, < 3.0)
299 | websocket (~> 1.0)
300 | solid_cable (3.0.4)
301 | actioncable (>= 7.2)
302 | activejob (>= 7.2)
303 | activerecord (>= 7.2)
304 | railties (>= 7.2)
305 | solid_cache (1.0.6)
306 | activejob (>= 7.2)
307 | activerecord (>= 7.2)
308 | railties (>= 7.2)
309 | solid_queue (1.1.0)
310 | activejob (>= 7.1)
311 | activerecord (>= 7.1)
312 | concurrent-ruby (>= 1.3.1)
313 | fugit (~> 1.11.0)
314 | railties (>= 7.1)
315 | thor (~> 1.3.1)
316 | sqlite3 (2.4.1-aarch64-linux-gnu)
317 | sqlite3 (2.4.1-aarch64-linux-musl)
318 | sqlite3 (2.4.1-arm-linux-gnu)
319 | sqlite3 (2.4.1-arm-linux-musl)
320 | sqlite3 (2.4.1-arm64-darwin)
321 | sqlite3 (2.4.1-x86-linux-gnu)
322 | sqlite3 (2.4.1-x86-linux-musl)
323 | sqlite3 (2.4.1-x86_64-darwin)
324 | sqlite3 (2.4.1-x86_64-linux-gnu)
325 | sqlite3 (2.4.1-x86_64-linux-musl)
326 | sshkit (1.23.2)
327 | base64
328 | net-scp (>= 1.1.2)
329 | net-sftp (>= 2.1.2)
330 | net-ssh (>= 2.8.0)
331 | ostruct
332 | stimulus-rails (1.3.4)
333 | railties (>= 6.0.0)
334 | stringio (3.1.2)
335 | thor (1.3.2)
336 | thruster (0.1.9)
337 | thruster (0.1.9-aarch64-linux)
338 | thruster (0.1.9-arm64-darwin)
339 | thruster (0.1.9-x86_64-darwin)
340 | thruster (0.1.9-x86_64-linux)
341 | timeout (0.4.3)
342 | turbo-rails (2.0.11)
343 | actionpack (>= 6.0.0)
344 | railties (>= 6.0.0)
345 | turso_libsql (0.2.0)
346 | ffi (~> 1.17)
347 | tzinfo (2.0.6)
348 | concurrent-ruby (~> 1.0)
349 | unicode-display_width (3.1.2)
350 | unicode-emoji (~> 4.0, >= 4.0.4)
351 | unicode-emoji (4.0.4)
352 | uri (1.0.2)
353 | useragent (0.16.11)
354 | web-console (4.2.1)
355 | actionview (>= 6.0.0)
356 | activemodel (>= 6.0.0)
357 | bindex (>= 0.4.0)
358 | railties (>= 6.0.0)
359 | websocket (1.2.11)
360 | websocket-driver (0.7.6)
361 | websocket-extensions (>= 0.1.0)
362 | websocket-extensions (0.1.5)
363 | xpath (3.2.0)
364 | nokogiri (~> 1.8)
365 | zeitwerk (2.7.1)
366 |
367 | PLATFORMS
368 | aarch64-linux
369 | aarch64-linux-gnu
370 | aarch64-linux-musl
371 | arm-linux
372 | arm-linux-gnu
373 | arm-linux-musl
374 | arm64-darwin
375 | x86-linux
376 | x86-linux-gnu
377 | x86-linux-musl
378 | x86_64-darwin
379 | x86_64-linux
380 | x86_64-linux-gnu
381 | x86_64-linux-musl
382 |
383 | DEPENDENCIES
384 | bootsnap
385 | brakeman
386 | capybara
387 | debug
388 | importmap-rails
389 | jbuilder
390 | kamal
391 | libsql_activerecord!
392 | propshaft
393 | puma (>= 5.0)
394 | rails (~> 8.0.1)
395 | rubocop-rails-omakase
396 | selenium-webdriver
397 | solid_cable
398 | solid_cache
399 | solid_queue
400 | sqlite3 (>= 2.1)
401 | stimulus-rails
402 | thruster
403 | turbo-rails
404 | tzinfo-data
405 | web-console
406 |
407 | BUNDLED WITH
408 | 2.5.22
409 |
--------------------------------------------------------------------------------