├── 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 | 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 | libSQL Ruby 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 | MIT License 24 | 25 | 26 | 27 | 28 | Discord 29 | 30 | 31 | 32 | 33 | Contributors 34 | 35 | 36 | 37 | 38 | Examples 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 | ![Contributors](https://contrib.nn.ci/api?repo=tursodatabase/libsql-activerecord) 88 | 89 | 90 | 91 | good first issue 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 |
105 | 106 |
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 |
105 | 106 |
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 |
105 | 106 |
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 |
105 | 106 |
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 |
105 | 106 |
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 | --------------------------------------------------------------------------------