diff --git a/README.md b/README.md index e07764e..290401c 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/twenty-cli/lib/twenty-cli/command/mixin/sqlite_mixin.rb b/twenty-cli/lib/twenty-cli/command/mixin/sqlite_mixin.rb index 501f209..e9b03cc 100644 --- a/twenty-cli/lib/twenty-cli/command/mixin/sqlite_mixin.rb +++ b/twenty-cli/lib/twenty-cli/command/mixin/sqlite_mixin.rb @@ -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 diff --git a/twenty-server/lib/twenty-server.rb b/twenty-server/lib/twenty-server.rb index de1fb52..03b746e 100644 --- a/twenty-server/lib/twenty-server.rb +++ b/twenty-server/lib/twenty-server.rb @@ -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 diff --git a/twenty-server/lib/twenty-server/graphql/mutation/create_task.rb b/twenty-server/lib/twenty-server/graphql/mutation/create_task.rb index cd7fdd3..d1052de 100644 --- a/twenty-server/lib/twenty-server/graphql/mutation/create_task.rb +++ b/twenty-server/lib/twenty-server/graphql/mutation/create_task.rb @@ -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]} diff --git a/twenty-server/lib/twenty-server/graphql/mutation/update_task.rb b/twenty-server/lib/twenty-server/graphql/mutation/update_task.rb index 86c15af..df1a680 100644 --- a/twenty-server/lib/twenty-server/graphql/mutation/update_task.rb +++ b/twenty-server/lib/twenty-server/graphql/mutation/update_task.rb @@ -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]} diff --git a/twenty-server/lib/twenty-server/graphql/type/query.rb b/twenty-server/lib/twenty-server/graphql/type/query.rb index 45a6440..fc9c5e8 100644 --- a/twenty-server/lib/twenty-server/graphql/type/query.rb +++ b/twenty-server/lib/twenty-server/graphql/type/query.rb @@ -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 diff --git a/twenty-server/lib/twenty-server/migration.rb b/twenty-server/lib/twenty-server/migration.rb index 9d3e587..0f2a779 100644 --- a/twenty-server/lib/twenty-server/migration.rb +++ b/twenty-server/lib/twenty-server/migration.rb @@ -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 diff --git a/twenty-server/lib/twenty-server/migration/1_create_projects.rb b/twenty-server/lib/twenty-server/migration/1_create_projects.rb index cd9f571..7eb2cb7 100644 --- a/twenty-server/lib/twenty-server/migration/1_create_projects.rb +++ b/twenty-server/lib/twenty-server/migration/1_create_projects.rb @@ -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 diff --git a/twenty-server/lib/twenty-server/migration/2_create_tasks.rb b/twenty-server/lib/twenty-server/migration/2_create_tasks.rb index 4cf2adb..9ee84c8 100644 --- a/twenty-server/lib/twenty-server/migration/2_create_tasks.rb +++ b/twenty-server/lib/twenty-server/migration/2_create_tasks.rb @@ -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 diff --git a/twenty-server/lib/twenty-server/migration/3_add_color_to_projects.rb b/twenty-server/lib/twenty-server/migration/3_add_color_to_projects.rb index 40af3f5..949151d 100644 --- a/twenty-server/lib/twenty-server/migration/3_add_color_to_projects.rb +++ b/twenty-server/lib/twenty-server/migration/3_add_color_to_projects.rb @@ -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 diff --git a/twenty-server/lib/twenty-server/model.rb b/twenty-server/lib/twenty-server/model.rb index 3518861..0e593b5 100644 --- a/twenty-server/lib/twenty-server/model.rb +++ b/twenty-server/lib/twenty-server/model.rb @@ -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" diff --git a/twenty-server/lib/twenty-server/model/mixin/colorable_mixin.rb b/twenty-server/lib/twenty-server/model/mixin/colorable_mixin.rb index 79f3496..0243f1f 100644 --- a/twenty-server/lib/twenty-server/model/mixin/colorable_mixin.rb +++ b/twenty-server/lib/twenty-server/model/mixin/colorable_mixin.rb @@ -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 diff --git a/twenty-server/lib/twenty-server/model/project.rb b/twenty-server/lib/twenty-server/model/project.rb index 2474729..055d397 100644 --- a/twenty-server/lib/twenty-server/model/project.rb +++ b/twenty-server/lib/twenty-server/model/project.rb @@ -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] diff --git a/twenty-server/lib/twenty-server/model/task.rb b/twenty-server/lib/twenty-server/model/task.rb index 01d1212..f82655e 100644 --- a/twenty-server/lib/twenty-server/model/task.rb +++ b/twenty-server/lib/twenty-server/model/task.rb @@ -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 diff --git a/twenty-server/twenty-server.gemspec b/twenty-server/twenty-server.gemspec index 6a4aca0..6b09db0 100644 --- a/twenty-server/twenty-server.gemspec +++ b/twenty-server/twenty-server.gemspec @@ -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" diff --git a/twenty-server/twenty-server.gemspec.erb b/twenty-server/twenty-server.gemspec.erb index 372b9cc..62a3475 100644 --- a/twenty-server/twenty-server.gemspec.erb +++ b/twenty-server/twenty-server.gemspec.erb @@ -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"