Replace ActiveRecord with Sequel

This commit is contained in:
0x1eef 2024-03-28 02:22:08 -03:00
parent 689b650a75
commit 4cbd4d6f39
16 changed files with 96 additions and 88 deletions

View file

@ -18,7 +18,7 @@ within your local network. It is both easy to use, and easy to install.
* The server is powered by [rack](https://github.com/rack/rack) and [puma](https://github.com/puma/puma):
- Accepts GraphQL requests at `/graphql`.
- Serves client (HTML, JS, CSS).
- Dependencies: ActiveRecord, SQLite3, ruby-graphql.
- Dependencies: Sequel, SQLite3, ruby-graphql.
* The client is a statically compiled [nanoc](https://github.com/nanoc/nanoc) website:
- Dependencies: webpack, typescript, react.
* The CLI controls the web server:

View file

@ -4,6 +4,8 @@ module Twenty::Command::SQLiteMixin
def run_command(options)
path = options.database || Twenty.default_database
Twenty.establish_connection(path:)
require "twenty-server/migration"
require "twenty-server/model"
super(options)
end
end

View file

@ -2,13 +2,8 @@
module Twenty
require "fileutils"
require "active_record"
require "sequel"
require_relative "twenty-server/path"
require_relative "twenty-server/graphql"
require_relative "twenty-server/rack"
require_relative "twenty-server/migration"
require_relative "twenty-server/model"
extend FileUtils
extend Path
##
@ -26,24 +21,24 @@ module Twenty
#
# @return [void]
def self.establish_connection(path:)
ActiveRecord::Base.establish_connection(
adapter: "sqlite3",
database: path,
pool: 16
@connection = Sequel.connect(
adapter: "sqlite",
database: path
)
end
##
# Prepares the parent directory of the database.
# @return [void]
# @api private
def self.prepare_dir
mkdir_p(datadir)
mkdir_p(tmpdir)
touch(default_database)
def self.connection
establish_connection unless @connection
@connection
end
begin
FileUtils.mkdir_p(datadir)
FileUtils.mkdir_p(tmpdir)
FileUtils.touch(default_database)
rescue => ex
warn "prepare_dir error: #{ex.message} (#{ex.class})"
end
private_class_method :prepare_dir
prepare_dir
require_relative "twenty-server/graphql"
require_relative "twenty-server/rack"
end

View file

@ -6,7 +6,7 @@ module Twenty::GraphQL::Mutation
argument :input, Twenty::GraphQL::Input::TaskInput
def resolve(input:)
Twenty::Task.new(input.to_h).save!
Twenty::Task.create(input.to_h)
{"errors" => []}
rescue => ex
{"errors" => [ex.message]}

View file

@ -7,8 +7,9 @@ module Twenty::GraphQL::Mutation
argument :input, Twenty::GraphQL::Input::TaskInput
def resolve(task_id:, input:)
task = Twenty::Task.find_by(id: task_id)
task.update!(input.to_h)
Twenty::Task
.with_pk!(task_id)
.update!(input.to_h)
{"errors" => []}
rescue => ex
{"errors" => [ex.message]}

View file

@ -11,15 +11,16 @@ module Twenty::GraphQL::Type
end
field :projects, [Project], null: false
def find_task(task_id:)
Twenty::Task.find_by(id: task_id)
Twenty::Task.with_pk!(task_id)
end
def tasks(status:, project_id: nil)
tasks = Twenty::Task
.where(status:)
.order(updated_at: :desc)
project_id ? tasks.where(project_id:) : tasks
.by_status(status)
.order("updated_at DESC")
(project_id ? tasks.where(project_id:) : tasks).all
end
def projects

View file

@ -1,34 +1,25 @@
# frozen_string_literal: true
module Twenty::Migration
require "sequel/extensions/migration"
##
# @return [String]
# Returns the path to twenty's migrations.
def self.migrations_path
[File.join(__dir__, "migration")]
File.join(__dir__, "migration")
end
##
# Runs migrations (if neccessary).
# @return [void]
def self.run!
context.migrate
Sequel::Migrator.run(Twenty.connection, migrations_path)
end
ActiveRecord.timestamped_migrations = false
##
# @return [Boolean]
# Returns true when there are pending migrations.
def self.pending_migrations?
context.open.pending_migrations.any?
Sequel::Migrator.is_current?(Twenty.connection, migrations_path)
end
##
# @return [ActiveRecord::MigrationContext]
# Returns an instance of
# {ActiveRecord::MigrationContext ActiveRecord::MigrationContext}.
def self.context
@context ||= ActiveRecord::MigrationContext.new(migrations_path)
end
private_class_method :context
end

View file

@ -1,16 +1,16 @@
# frozen_string_literal: true
class CreateProjects < ActiveRecord::Migration[7.1]
def up
create_table(:projects) do |t|
t.string :name, null: false
t.string :path, null: false
t.timestamps
Sequel.migration do
up do
create_table(:projects) do
primary_key :id
String :name, null: false
String :path, null: false
DateTime :created_at
DateTime :updated_at
end
add_index :projects, [:name, :path], unique: true
end
def down
down do
drop_table(:projects)
end
end

View file

@ -1,17 +1,19 @@
# frozen_string_literal: true
class CreateTasks < ActiveRecord::Migration[7.1]
def up
Sequel.migration do
up do
create_table(:tasks) do |t|
t.string :title, null: false
t.text :content, null: false
t.integer :status, null: false, default: 0
t.belongs_to :project, null: false
t.timestamps
primary_key :id
String :title, null: false
Text :content, null: false
Integer :status, null: false, default: 0
DateTime :created_at
DateTime :updated_at
foreign_key :project_id, :projects, null: false
end
end
def down
down do
drop_table(:tasks)
end
end

View file

@ -1,12 +1,12 @@
# frozen_string_literal: true
class AddColorToProjects < ActiveRecord::Migration[7.1]
def up
Sequel.migration do
up do
default = Twenty::ColorableMixin.random_color
add_column :projects, :color, :string, null: false, default:
end
def down
down do
drop_column :projects, :color
end
end

View file

@ -1,6 +1,11 @@
# frozen_string_literal: true
class Twenty::Model < ActiveRecord::Base
module Twenty::Model
def self.included(model)
model.plugin(:validation_class_methods)
model.plugin(:timestamps, update_on_create: true)
end
require_relative "model/mixin/colorable_mixin"
require_relative "model/project"
require_relative "model/task"

View file

@ -14,8 +14,9 @@ module Twenty::ColorableMixin
"#993366", "#2200AA", "#557788", "#998877", "#BB4400"
]
def self.included(klass)
klass.before_validation :set_random_color, on: :create
def before_validation
super if defined?(super)
set_random_color
end
def random_color
@ -25,6 +26,7 @@ module Twenty::ColorableMixin
private
def set_random_color
return if id
self.color = random_color
end
end

View file

@ -1,17 +1,12 @@
# frozen_string_literal: true
class Twenty::Project < Twenty::Model
class Twenty::Project < Sequel::Model
include Twenty::Model
include Twenty::ColorableMixin
self.table_name = "projects"
##
# Validations
validates :name, presence: true
validates :path, presence: true
##
# Associations
has_many :tasks, class_name: "Twenty::Task"
validates_presence_of :name
validates_presence_of :path
one_to_many :tasks, class_name: "Twenty::Task"
##
# @return [String]

View file

@ -1,18 +1,32 @@
# frozen_string_literal: true
class Twenty::Task < Twenty::Model
self.table_name = "tasks"
class Twenty::Task < Sequel::Model
include Twenty::Model
STATUS = {backlog: 0, ready: 1, in_progress: 2, complete: 3}
enum :status, STATUS, default: :backlog
plugin(:enum)
STATUS_MAP = {backlog: 0, ready: 1, in_progress: 2, complete: 3}
STATUS_KEYS = STATUS_MAP.keys
enum :status, STATUS_MAP
##
# Validations
validates :title, presence: true
validates :content, presence: true
validates :project, presence: true
def self.by_status(status)
if STATUS_KEYS.any? { _1.to_s == status.to_s }
public_send(status)
else
where(id: nil)
end
end
##
# Associations
belongs_to :project, class_name: "Twenty::Project"
validates_presence_of :title
validates_presence_of :content
validates_presence_of :project
validates_inclusion_of :status, in: [STATUS_KEYS, *STATUS_KEYS.map(&:to_s)]
many_to_one :project, class_name: "Twenty::Project"
def status=(v)
super(v.to_sym)
end
def status
super.to_s
end
end

View file

@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
gem.require_paths = ["lib"]
gem.summary = "twenty: server-side component"
gem.description = gem.summary
gem.add_runtime_dependency "activerecord", "~> 7.1"
gem.add_runtime_dependency "sequel", "~> 5.78"
gem.add_runtime_dependency "sqlite3", "~> 1.6"
gem.add_runtime_dependency "graphql", "~> 2.2"
gem.add_runtime_dependency "server.rb", "~> 0.1"

View file

@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
gem.require_paths = ["lib"]
gem.summary = "twenty: server-side component"
gem.description = gem.summary
gem.add_runtime_dependency "activerecord", "~> 7.1"
gem.add_runtime_dependency "sequel", "~> 5.78"
gem.add_runtime_dependency "sqlite3", "~> 1.6"
gem.add_runtime_dependency "graphql", "~> 2.2"
gem.add_runtime_dependency "server.rb", "~> 0.1"