Replace ActiveRecord with Sequel
This commit is contained in:
parent
689b650a75
commit
4cbd4d6f39
16 changed files with 96 additions and 88 deletions
|
@ -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):
|
* The server is powered by [rack](https://github.com/rack/rack) and [puma](https://github.com/puma/puma):
|
||||||
- Accepts GraphQL requests at `/graphql`.
|
- Accepts GraphQL requests at `/graphql`.
|
||||||
- Serves client (HTML, JS, CSS).
|
- 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:
|
* The client is a statically compiled [nanoc](https://github.com/nanoc/nanoc) website:
|
||||||
- Dependencies: webpack, typescript, react.
|
- Dependencies: webpack, typescript, react.
|
||||||
* The CLI controls the web server:
|
* The CLI controls the web server:
|
||||||
|
|
|
@ -4,6 +4,8 @@ module Twenty::Command::SQLiteMixin
|
||||||
def run_command(options)
|
def run_command(options)
|
||||||
path = options.database || Twenty.default_database
|
path = options.database || Twenty.default_database
|
||||||
Twenty.establish_connection(path:)
|
Twenty.establish_connection(path:)
|
||||||
|
require "twenty-server/migration"
|
||||||
|
require "twenty-server/model"
|
||||||
super(options)
|
super(options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,13 +2,8 @@
|
||||||
|
|
||||||
module Twenty
|
module Twenty
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
require "active_record"
|
require "sequel"
|
||||||
require_relative "twenty-server/path"
|
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
|
extend Path
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -26,24 +21,24 @@ module Twenty
|
||||||
#
|
#
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def self.establish_connection(path:)
|
def self.establish_connection(path:)
|
||||||
ActiveRecord::Base.establish_connection(
|
@connection = Sequel.connect(
|
||||||
adapter: "sqlite3",
|
adapter: "sqlite",
|
||||||
database: path,
|
database: path
|
||||||
pool: 16
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
def self.connection
|
||||||
# Prepares the parent directory of the database.
|
establish_connection unless @connection
|
||||||
# @return [void]
|
@connection
|
||||||
# @api private
|
end
|
||||||
def self.prepare_dir
|
|
||||||
mkdir_p(datadir)
|
begin
|
||||||
mkdir_p(tmpdir)
|
FileUtils.mkdir_p(datadir)
|
||||||
touch(default_database)
|
FileUtils.mkdir_p(tmpdir)
|
||||||
|
FileUtils.touch(default_database)
|
||||||
rescue => ex
|
rescue => ex
|
||||||
warn "prepare_dir error: #{ex.message} (#{ex.class})"
|
warn "prepare_dir error: #{ex.message} (#{ex.class})"
|
||||||
end
|
end
|
||||||
private_class_method :prepare_dir
|
require_relative "twenty-server/graphql"
|
||||||
prepare_dir
|
require_relative "twenty-server/rack"
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,7 @@ module Twenty::GraphQL::Mutation
|
||||||
argument :input, Twenty::GraphQL::Input::TaskInput
|
argument :input, Twenty::GraphQL::Input::TaskInput
|
||||||
|
|
||||||
def resolve(input:)
|
def resolve(input:)
|
||||||
Twenty::Task.new(input.to_h).save!
|
Twenty::Task.create(input.to_h)
|
||||||
{"errors" => []}
|
{"errors" => []}
|
||||||
rescue => ex
|
rescue => ex
|
||||||
{"errors" => [ex.message]}
|
{"errors" => [ex.message]}
|
||||||
|
|
|
@ -7,8 +7,9 @@ module Twenty::GraphQL::Mutation
|
||||||
argument :input, Twenty::GraphQL::Input::TaskInput
|
argument :input, Twenty::GraphQL::Input::TaskInput
|
||||||
|
|
||||||
def resolve(task_id:, input:)
|
def resolve(task_id:, input:)
|
||||||
task = Twenty::Task.find_by(id: task_id)
|
Twenty::Task
|
||||||
task.update!(input.to_h)
|
.with_pk!(task_id)
|
||||||
|
.update!(input.to_h)
|
||||||
{"errors" => []}
|
{"errors" => []}
|
||||||
rescue => ex
|
rescue => ex
|
||||||
{"errors" => [ex.message]}
|
{"errors" => [ex.message]}
|
||||||
|
|
|
@ -11,15 +11,16 @@ module Twenty::GraphQL::Type
|
||||||
end
|
end
|
||||||
field :projects, [Project], null: false
|
field :projects, [Project], null: false
|
||||||
|
|
||||||
|
|
||||||
def find_task(task_id:)
|
def find_task(task_id:)
|
||||||
Twenty::Task.find_by(id: task_id)
|
Twenty::Task.with_pk!(task_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def tasks(status:, project_id: nil)
|
def tasks(status:, project_id: nil)
|
||||||
tasks = Twenty::Task
|
tasks = Twenty::Task
|
||||||
.where(status:)
|
.by_status(status)
|
||||||
.order(updated_at: :desc)
|
.order("updated_at DESC")
|
||||||
project_id ? tasks.where(project_id:) : tasks
|
(project_id ? tasks.where(project_id:) : tasks).all
|
||||||
end
|
end
|
||||||
|
|
||||||
def projects
|
def projects
|
||||||
|
|
|
@ -1,34 +1,25 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Twenty::Migration
|
module Twenty::Migration
|
||||||
|
require "sequel/extensions/migration"
|
||||||
##
|
##
|
||||||
# @return [String]
|
# @return [String]
|
||||||
# Returns the path to twenty's migrations.
|
# Returns the path to twenty's migrations.
|
||||||
def self.migrations_path
|
def self.migrations_path
|
||||||
[File.join(__dir__, "migration")]
|
File.join(__dir__, "migration")
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Runs migrations (if neccessary).
|
# Runs migrations (if neccessary).
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def self.run!
|
def self.run!
|
||||||
context.migrate
|
Sequel::Migrator.run(Twenty.connection, migrations_path)
|
||||||
end
|
end
|
||||||
ActiveRecord.timestamped_migrations = false
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# @return [Boolean]
|
# @return [Boolean]
|
||||||
# Returns true when there are pending migrations.
|
# Returns true when there are pending migrations.
|
||||||
def self.pending_migrations?
|
def self.pending_migrations?
|
||||||
context.open.pending_migrations.any?
|
Sequel::Migrator.is_current?(Twenty.connection, migrations_path)
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
# frozen_string_literal: true
|
Sequel.migration do
|
||||||
|
up do
|
||||||
class CreateProjects < ActiveRecord::Migration[7.1]
|
create_table(:projects) do
|
||||||
def up
|
primary_key :id
|
||||||
create_table(:projects) do |t|
|
String :name, null: false
|
||||||
t.string :name, null: false
|
String :path, null: false
|
||||||
t.string :path, null: false
|
DateTime :created_at
|
||||||
t.timestamps
|
DateTime :updated_at
|
||||||
end
|
end
|
||||||
add_index :projects, [:name, :path], unique: true
|
add_index :projects, [:name, :path], unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
def down
|
down do
|
||||||
drop_table(:projects)
|
drop_table(:projects)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CreateTasks < ActiveRecord::Migration[7.1]
|
Sequel.migration do
|
||||||
def up
|
up do
|
||||||
create_table(:tasks) do |t|
|
create_table(:tasks) do |t|
|
||||||
t.string :title, null: false
|
primary_key :id
|
||||||
t.text :content, null: false
|
String :title, null: false
|
||||||
t.integer :status, null: false, default: 0
|
Text :content, null: false
|
||||||
t.belongs_to :project, null: false
|
Integer :status, null: false, default: 0
|
||||||
t.timestamps
|
DateTime :created_at
|
||||||
|
DateTime :updated_at
|
||||||
|
foreign_key :project_id, :projects, null: false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def down
|
down do
|
||||||
drop_table(:tasks)
|
drop_table(:tasks)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class AddColorToProjects < ActiveRecord::Migration[7.1]
|
Sequel.migration do
|
||||||
def up
|
up do
|
||||||
default = Twenty::ColorableMixin.random_color
|
default = Twenty::ColorableMixin.random_color
|
||||||
add_column :projects, :color, :string, null: false, default:
|
add_column :projects, :color, :string, null: false, default:
|
||||||
end
|
end
|
||||||
|
|
||||||
def down
|
down do
|
||||||
drop_column :projects, :color
|
drop_column :projects, :color
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
# frozen_string_literal: true
|
# 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/mixin/colorable_mixin"
|
||||||
require_relative "model/project"
|
require_relative "model/project"
|
||||||
require_relative "model/task"
|
require_relative "model/task"
|
||||||
|
|
|
@ -14,8 +14,9 @@ module Twenty::ColorableMixin
|
||||||
"#993366", "#2200AA", "#557788", "#998877", "#BB4400"
|
"#993366", "#2200AA", "#557788", "#998877", "#BB4400"
|
||||||
]
|
]
|
||||||
|
|
||||||
def self.included(klass)
|
def before_validation
|
||||||
klass.before_validation :set_random_color, on: :create
|
super if defined?(super)
|
||||||
|
set_random_color
|
||||||
end
|
end
|
||||||
|
|
||||||
def random_color
|
def random_color
|
||||||
|
@ -25,6 +26,7 @@ module Twenty::ColorableMixin
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_random_color
|
def set_random_color
|
||||||
|
return if id
|
||||||
self.color = random_color
|
self.color = random_color
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,17 +1,12 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Twenty::Project < Twenty::Model
|
class Twenty::Project < Sequel::Model
|
||||||
|
include Twenty::Model
|
||||||
include Twenty::ColorableMixin
|
include Twenty::ColorableMixin
|
||||||
self.table_name = "projects"
|
|
||||||
|
|
||||||
##
|
validates_presence_of :name
|
||||||
# Validations
|
validates_presence_of :path
|
||||||
validates :name, presence: true
|
one_to_many :tasks, class_name: "Twenty::Task"
|
||||||
validates :path, presence: true
|
|
||||||
|
|
||||||
##
|
|
||||||
# Associations
|
|
||||||
has_many :tasks, class_name: "Twenty::Task"
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# @return [String]
|
# @return [String]
|
||||||
|
|
|
@ -1,18 +1,32 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Twenty::Task < Twenty::Model
|
class Twenty::Task < Sequel::Model
|
||||||
self.table_name = "tasks"
|
include Twenty::Model
|
||||||
|
|
||||||
STATUS = {backlog: 0, ready: 1, in_progress: 2, complete: 3}
|
plugin(:enum)
|
||||||
enum :status, STATUS, default: :backlog
|
STATUS_MAP = {backlog: 0, ready: 1, in_progress: 2, complete: 3}
|
||||||
|
STATUS_KEYS = STATUS_MAP.keys
|
||||||
|
enum :status, STATUS_MAP
|
||||||
|
|
||||||
##
|
def self.by_status(status)
|
||||||
# Validations
|
if STATUS_KEYS.any? { _1.to_s == status.to_s }
|
||||||
validates :title, presence: true
|
public_send(status)
|
||||||
validates :content, presence: true
|
else
|
||||||
validates :project, presence: true
|
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
|
end
|
||||||
|
|
|
@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
|
||||||
gem.require_paths = ["lib"]
|
gem.require_paths = ["lib"]
|
||||||
gem.summary = "twenty: server-side component"
|
gem.summary = "twenty: server-side component"
|
||||||
gem.description = gem.summary
|
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 "sqlite3", "~> 1.6"
|
||||||
gem.add_runtime_dependency "graphql", "~> 2.2"
|
gem.add_runtime_dependency "graphql", "~> 2.2"
|
||||||
gem.add_runtime_dependency "server.rb", "~> 0.1"
|
gem.add_runtime_dependency "server.rb", "~> 0.1"
|
||||||
|
|
|
@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
|
||||||
gem.require_paths = ["lib"]
|
gem.require_paths = ["lib"]
|
||||||
gem.summary = "twenty: server-side component"
|
gem.summary = "twenty: server-side component"
|
||||||
gem.description = gem.summary
|
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 "sqlite3", "~> 1.6"
|
||||||
gem.add_runtime_dependency "graphql", "~> 2.2"
|
gem.add_runtime_dependency "graphql", "~> 2.2"
|
||||||
gem.add_runtime_dependency "server.rb", "~> 0.1"
|
gem.add_runtime_dependency "server.rb", "~> 0.1"
|
||||||
|
|
Loading…
Reference in a new issue