ratatui_ruby 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.builds/ruby-3.2.yml +52 -0
- data/.builds/ruby-3.3.yml +52 -0
- data/.builds/ruby-3.4.yml +52 -0
- data/.builds/ruby-4.0.0-preview3.yml +53 -0
- data/AGENTS.md +2 -2
- data/README.md +33 -13
- data/REUSE.toml +5 -0
- data/Rakefile +3 -100
- data/docs/images/examples-calendar_demo.rb.png +0 -0
- data/docs/images/examples-chart_demo.rb.png +0 -0
- data/docs/images/examples-list_styles.rb.png +0 -0
- data/docs/images/examples-quickstart_lifecycle.rb.png +0 -0
- data/docs/images/examples-stock_ticker.rb.png +0 -0
- data/docs/quickstart.md +57 -11
- data/examples/analytics.rb +2 -1
- data/examples/calendar_demo.rb +55 -0
- data/examples/chart_demo.rb +84 -0
- data/examples/list_styles.rb +66 -0
- data/examples/login_form.rb +2 -1
- data/examples/quickstart_dsl.rb +30 -0
- data/examples/quickstart_lifecycle.rb +40 -0
- data/examples/readme_usage.rb +21 -0
- data/examples/stock_ticker.rb +13 -5
- data/examples/system_monitor.rb +2 -1
- data/examples/test_calendar_demo.rb +66 -0
- data/examples/test_list_styles.rb +61 -0
- data/ext/ratatui_ruby/.cargo/config.toml +5 -0
- data/ext/ratatui_ruby/Cargo.lock +94 -1
- data/ext/ratatui_ruby/Cargo.toml +3 -2
- data/ext/ratatui_ruby/extconf.rb +1 -1
- data/ext/ratatui_ruby/src/events.rs +4 -1
- data/ext/ratatui_ruby/src/rendering.rs +4 -1
- data/ext/ratatui_ruby/src/terminal.rs +4 -6
- data/ext/ratatui_ruby/src/widgets/calendar.rs +81 -0
- data/ext/ratatui_ruby/src/widgets/chart.rs +253 -0
- data/ext/ratatui_ruby/src/widgets/list.rs +41 -4
- data/ext/ratatui_ruby/src/widgets/mod.rs +2 -1
- data/lib/ratatui_ruby/dsl.rb +62 -0
- data/lib/ratatui_ruby/schema/calendar.rb +26 -0
- data/lib/ratatui_ruby/schema/chart.rb +81 -0
- data/lib/ratatui_ruby/schema/list.rb +8 -2
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby.rb +21 -1
- data/mise.toml +8 -0
- data/sig/ratatui_ruby/schema/calendar.rbs +13 -0
- data/sig/ratatui_ruby/schema/{line_chart.rbs → chart.rbs} +20 -1
- data/sig/ratatui_ruby/schema/list.rbs +4 -1
- data/tasks/bump/cargo_lockfile.rb +19 -0
- data/tasks/bump/manifest.rb +23 -0
- data/tasks/bump/ruby_gem.rb +39 -0
- data/tasks/bump/sem_ver.rb +28 -0
- data/tasks/bump.rake +45 -0
- data/tasks/doc.rake +24 -0
- data/tasks/extension.rake +12 -0
- data/tasks/lint.rake +49 -0
- data/tasks/rdoc_config.rb +15 -0
- data/tasks/resources/build.yml.erb +65 -0
- data/tasks/resources/index.html.erb +38 -0
- data/tasks/resources/rubies.yml +7 -0
- data/tasks/sourcehut.rake +29 -0
- data/tasks/test.rake +31 -0
- data/tasks/website/index_page.rb +28 -0
- data/tasks/website/version.rb +116 -0
- data/tasks/website/versioned_documentation.rb +48 -0
- data/tasks/website/website.rb +50 -0
- data/tasks/website.rake +26 -0
- metadata +51 -10
- data/.build.yml +0 -34
- data/.ruby-version +0 -1
- data/ext/ratatui_ruby/src/widgets/linechart.rs +0 -154
- data/lib/ratatui_ruby/schema/line_chart.rb +0 -41
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
2
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
module RatatuiRuby
|
|
5
|
+
class Calendar < Data
|
|
6
|
+
attr_reader year: Integer
|
|
7
|
+
attr_reader month: Integer
|
|
8
|
+
attr_reader day_style: Style?
|
|
9
|
+
attr_reader header_style: Style?
|
|
10
|
+
attr_reader block: Block?
|
|
11
|
+
def self.new: (year: Integer, month: Integer, ?day_style: Style?, ?header_style: Style?, ?block: Block?) -> Calendar
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -2,11 +2,30 @@
|
|
|
2
2
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
4
|
module RatatuiRuby
|
|
5
|
+
class Axis < Data
|
|
6
|
+
attr_reader title: String
|
|
7
|
+
attr_reader bounds: [Float, Float]
|
|
8
|
+
attr_reader labels: Array[String]
|
|
9
|
+
attr_reader style: Style?
|
|
10
|
+
def self.new: (?title: String, ?bounds: [Float, Float], ?labels: Array[String], ?style: Style?) -> Axis
|
|
11
|
+
end
|
|
12
|
+
|
|
5
13
|
class Dataset < Data
|
|
6
14
|
attr_reader name: String | Symbol
|
|
7
15
|
attr_reader data: Array[[Float, Float]]
|
|
8
16
|
attr_reader color: String | Symbol
|
|
9
|
-
|
|
17
|
+
attr_reader marker: Symbol
|
|
18
|
+
attr_reader graph_type: Symbol
|
|
19
|
+
def self.new: (name: String | Symbol, data: Array[[Float, Float]], ?color: String | Symbol, ?marker: Symbol, ?graph_type: Symbol) -> Dataset
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class Chart < Data
|
|
23
|
+
attr_reader datasets: Array[Dataset]
|
|
24
|
+
attr_reader x_axis: Axis
|
|
25
|
+
attr_reader y_axis: Axis
|
|
26
|
+
attr_reader block: Block?
|
|
27
|
+
attr_reader style: Style?
|
|
28
|
+
def self.new: (datasets: Array[Dataset], x_axis: Axis, y_axis: Axis, ?block: Block?, ?style: Style?) -> Chart
|
|
10
29
|
end
|
|
11
30
|
|
|
12
31
|
class LineChart < Data
|
|
@@ -5,7 +5,10 @@ module RatatuiRuby
|
|
|
5
5
|
class List < Data
|
|
6
6
|
attr_reader items: Array[String]
|
|
7
7
|
attr_reader selected_index: Integer?
|
|
8
|
+
attr_reader style: Style?
|
|
9
|
+
attr_reader highlight_style: Style?
|
|
10
|
+
attr_reader highlight_symbol: String?
|
|
8
11
|
attr_reader block: Block?
|
|
9
|
-
def self.new: (?items: Array[String], ?selected_index: Integer?, ?block: Block?) -> List
|
|
12
|
+
def self.new: (?items: Array[String], ?selected_index: Integer?, ?style: Style?, ?highlight_style: Style?, ?highlight_symbol: String?, ?block: Block?) -> List
|
|
10
13
|
end
|
|
11
14
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
# Lockfiles need to be refreshed by a tool after Manifests are changed.
|
|
7
|
+
class CargoLockfile < Data.define(:path, :dir, :name)
|
|
8
|
+
def exists?
|
|
9
|
+
File.exist?(path)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def refresh
|
|
13
|
+
return unless exists?
|
|
14
|
+
|
|
15
|
+
Dir.chdir(dir) do
|
|
16
|
+
system("cargo update -p #{name} --offline")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
# Manifests hold a copy of the version number and should be changed manually.
|
|
7
|
+
# Use Regexp lookarounds in `pattern` to match the version number.
|
|
8
|
+
class Manifest < Data.define(:path, :pattern, :primary)
|
|
9
|
+
def read
|
|
10
|
+
File.read(path)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(path:, pattern:, primary: false)
|
|
14
|
+
super
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def write(version)
|
|
18
|
+
return unless File.exist?(path)
|
|
19
|
+
|
|
20
|
+
new_content = read.gsub(pattern, version.to_s)
|
|
21
|
+
File.write(path, new_content)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
class RubyGem
|
|
7
|
+
def initialize(manifests:, lockfile:)
|
|
8
|
+
raise ArgumentError, "Must have exactly one primary manifest" unless manifests.count(&:primary) == 1
|
|
9
|
+
@manifests = manifests
|
|
10
|
+
@lockfile = lockfile
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def version
|
|
14
|
+
source = @manifests.find(&:primary)
|
|
15
|
+
content = source.read
|
|
16
|
+
match = content.match(source.pattern)
|
|
17
|
+
raise "Version missing in manifest #{source.path}" unless match
|
|
18
|
+
|
|
19
|
+
segments = Gem::Version.new(match[0]).segments
|
|
20
|
+
SemVer.new(segments.fill(0, 3).first(3))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def bump(segment)
|
|
24
|
+
target = version.next(segment)
|
|
25
|
+
|
|
26
|
+
puts "Bumping #{segment}: #{version} -> #{target}"
|
|
27
|
+
@manifests.each { |manifest| manifest.write(target) }
|
|
28
|
+
@lockfile.refresh
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def set(version_string)
|
|
32
|
+
segments = Gem::Version.new(version_string).segments.fill(0, 3).first(3)
|
|
33
|
+
target = SemVer.new(segments)
|
|
34
|
+
|
|
35
|
+
puts "Setting version: #{version} -> #{target}"
|
|
36
|
+
@manifests.each { |manifest| manifest.write(target) }
|
|
37
|
+
@lockfile.refresh
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
# See https://semver.org/spec/v2.0.0.html
|
|
7
|
+
class SemVer
|
|
8
|
+
SEGMENTS = [:major, :minor, :patch].freeze
|
|
9
|
+
|
|
10
|
+
def initialize(segments)
|
|
11
|
+
@segments = segments
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def next(segment)
|
|
15
|
+
index = SEGMENTS.index(segment)
|
|
16
|
+
raise ArgumentError, "Invalid segment: #{segment}" unless index
|
|
17
|
+
|
|
18
|
+
new_segments = @segments.dup
|
|
19
|
+
new_segments[index] += 1
|
|
20
|
+
new_segments.fill(0, (index + 1)..2)
|
|
21
|
+
|
|
22
|
+
SemVer.new(new_segments)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_s
|
|
26
|
+
@segments.join(".")
|
|
27
|
+
end
|
|
28
|
+
end
|
data/tasks/bump.rake
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
require "rubygems"
|
|
7
|
+
|
|
8
|
+
require_relative "bump/sem_ver"
|
|
9
|
+
require_relative "bump/manifest"
|
|
10
|
+
require_relative "bump/cargo_lockfile"
|
|
11
|
+
require_relative "bump/ruby_gem"
|
|
12
|
+
|
|
13
|
+
namespace :bump do
|
|
14
|
+
ratatuiRuby = RubyGem.new(
|
|
15
|
+
manifests: [
|
|
16
|
+
Manifest.new(
|
|
17
|
+
path: "lib/ratatui_ruby/version.rb",
|
|
18
|
+
pattern: /(?<=VERSION = ")[^"]+(?=")/,
|
|
19
|
+
primary: true
|
|
20
|
+
),
|
|
21
|
+
Manifest.new(
|
|
22
|
+
path: "ext/ratatui_ruby/Cargo.toml",
|
|
23
|
+
pattern: /(?<=^version = ")[^"]+(?=")/,
|
|
24
|
+
primary: false
|
|
25
|
+
),
|
|
26
|
+
],
|
|
27
|
+
lockfile: CargoLockfile.new(
|
|
28
|
+
path: "ext/ratatui_ruby/Cargo.lock",
|
|
29
|
+
dir: "ext/ratatui_ruby",
|
|
30
|
+
name: "ratatui_ruby"
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
SemVer::SEGMENTS.each do |segment|
|
|
35
|
+
desc "Bump #{segment} version"
|
|
36
|
+
task segment do
|
|
37
|
+
ratatuiRuby.bump(segment)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
desc "Set exact version (e.g. rake bump:exact[0.1.0])"
|
|
42
|
+
task :exact, [:version] do |_, args|
|
|
43
|
+
ratatuiRuby.set(args[:version])
|
|
44
|
+
end
|
|
45
|
+
end
|
data/tasks/doc.rake
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
require "rdoc/task"
|
|
7
|
+
|
|
8
|
+
require_relative "rdoc_config"
|
|
9
|
+
|
|
10
|
+
RDoc::Task.new do |rdoc|
|
|
11
|
+
rdoc.rdoc_dir = "doc"
|
|
12
|
+
rdoc.main = RDocConfig::MAIN
|
|
13
|
+
rdoc.rdoc_files.include(RDocConfig::RDOC_FILES)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
task :copy_doc_images do
|
|
17
|
+
if Dir.exist?("docs/images")
|
|
18
|
+
FileUtils.mkdir_p "doc/docs/images"
|
|
19
|
+
FileUtils.cp_r Dir["docs/images/*.png"], "doc/docs/images"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Rake::Task[:rdoc].enhance [:copy_doc_images]
|
|
24
|
+
Rake::Task[:rerdoc].enhance [:copy_doc_images]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
require "rake/extensiontask"
|
|
7
|
+
|
|
8
|
+
spec = Gem::Specification.load("ratatui_ruby.gemspec")
|
|
9
|
+
Rake::ExtensionTask.new("ratatui_ruby", spec) do |ext|
|
|
10
|
+
ext.lib_dir = "lib/ratatui_ruby"
|
|
11
|
+
ext.ext_dir = "ext/ratatui_ruby"
|
|
12
|
+
end
|
data/tasks/lint.rake
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
require "rubocop/rake_task"
|
|
7
|
+
require "rubycritic/rake_task"
|
|
8
|
+
require "inch/rake"
|
|
9
|
+
|
|
10
|
+
RuboCop::RakeTask.new
|
|
11
|
+
|
|
12
|
+
RubyCritic::RakeTask.new do |task|
|
|
13
|
+
task.options = "--no-browser"
|
|
14
|
+
task.paths = FileList.new.include("exe/**/*.rb", "lib/**/*.rb", "sig/**/*.rbs")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Inch::Rake::Suggest.new("doc:suggest", "exe/**/*.rb", "lib/**/*.rb", "sig/**/*.rbs") do |suggest|
|
|
18
|
+
suggest.args << ""
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
namespace :cargo do
|
|
22
|
+
desc "Run cargo fmt"
|
|
23
|
+
task :fmt do
|
|
24
|
+
sh "cd ext/ratatui_ruby && cargo fmt --all -- --check"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
desc "Run cargo clippy"
|
|
28
|
+
task :clippy do
|
|
29
|
+
sh "cd ext/ratatui_ruby && cargo clippy -- -D warnings"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
namespace :reuse do
|
|
34
|
+
desc "Run the REUSE Tool to confirm REUSE compliance"
|
|
35
|
+
task :lint do
|
|
36
|
+
sh "reuse lint"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
task(:reuse) { Rake::Task["reuse:lint"].invoke }
|
|
40
|
+
|
|
41
|
+
namespace :lint do
|
|
42
|
+
task docs: %w[rubycritic rdoc:coverage reuse:lint]
|
|
43
|
+
task code: %w[rubocop rubycritic cargo:fmt cargo:clippy cargo:test]
|
|
44
|
+
task licenses: %w[reuse:lint]
|
|
45
|
+
task all: %w[docs code licenses]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
desc "Run all lint tasks"
|
|
49
|
+
task(:lint) { Rake::Task["lint:all"].invoke }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
module RDocConfig
|
|
7
|
+
RDOC_FILES = %w[
|
|
8
|
+
**/*.md
|
|
9
|
+
**/*.rdoc
|
|
10
|
+
lib/**/*.rb
|
|
11
|
+
exe/**/*
|
|
12
|
+
].freeze
|
|
13
|
+
|
|
14
|
+
MAIN = "README.md"
|
|
15
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
2
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
image: alpine/edge
|
|
5
|
+
packages:
|
|
6
|
+
- bash
|
|
7
|
+
- build-base
|
|
8
|
+
- curl
|
|
9
|
+
- openssl-dev
|
|
10
|
+
- yaml-dev
|
|
11
|
+
- zlib-dev
|
|
12
|
+
- readline-dev
|
|
13
|
+
- gdbm-dev
|
|
14
|
+
- ncurses-dev
|
|
15
|
+
- libffi-dev
|
|
16
|
+
- clang-dev
|
|
17
|
+
- git
|
|
18
|
+
artifacts:
|
|
19
|
+
- ratatui_ruby/pkg/<%= gem_filename %>
|
|
20
|
+
sources:
|
|
21
|
+
- https://git.sr.ht/~kerrick/ratatui_ruby
|
|
22
|
+
tasks:
|
|
23
|
+
- setup: |
|
|
24
|
+
curl https://mise.jdx.dev/install.sh | sh
|
|
25
|
+
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.buildenv
|
|
26
|
+
echo 'eval "$($HOME/.local/bin/mise activate bash)"' >> ~/.buildenv
|
|
27
|
+
. ~/.buildenv
|
|
28
|
+
export RUSTFLAGS="-C target-feature=-crt-static"
|
|
29
|
+
export CI="true"
|
|
30
|
+
cd ratatui_ruby
|
|
31
|
+
sed -i 's/ruby = .*/ruby = "<%= ruby_version %>"/' mise.toml
|
|
32
|
+
mise install
|
|
33
|
+
mise x -- pip install reuse
|
|
34
|
+
mise x -- gem install bundler
|
|
35
|
+
mise reshim
|
|
36
|
+
mise x -- bundle config set --local frozen 'true'
|
|
37
|
+
mise x -- bundle install
|
|
38
|
+
<% if ruby_version.start_with?("4.0") -%>
|
|
39
|
+
# We allow this to fail so we can see logs without stopping the CI pipeline
|
|
40
|
+
mise x -- bundle exec rake compile || echo "⚠️ Compilation failed (Allowed Failure)"
|
|
41
|
+
<% else -%>
|
|
42
|
+
mise x -- bundle exec rake compile
|
|
43
|
+
<% end -%>
|
|
44
|
+
- test: |
|
|
45
|
+
. ~/.buildenv
|
|
46
|
+
cd ratatui_ruby
|
|
47
|
+
echo "Testing Ruby <%= ruby_version %>"
|
|
48
|
+
<% if ruby_version.start_with?("4.0") -%>
|
|
49
|
+
mise x -- bundle exec rake test || true
|
|
50
|
+
<% else -%>
|
|
51
|
+
mise x -- bundle exec rake test
|
|
52
|
+
<% end -%>
|
|
53
|
+
- lint: |
|
|
54
|
+
. ~/.buildenv
|
|
55
|
+
cd ratatui_ruby
|
|
56
|
+
echo "Linting Ruby <%= ruby_version %>"
|
|
57
|
+
<% if ruby_version.start_with?("4.0") -%>
|
|
58
|
+
mise x -- bundle exec rake lint || true
|
|
59
|
+
<% else -%>
|
|
60
|
+
mise x -- bundle exec rake lint
|
|
61
|
+
<% end -%>
|
|
62
|
+
- package: |
|
|
63
|
+
. ~/.buildenv
|
|
64
|
+
cd ratatui_ruby
|
|
65
|
+
mise x -- bundle exec rake build
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title><%= project_name %> documentation</title>
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<style>
|
|
7
|
+
:root { color-scheme: light dark; }
|
|
8
|
+
body { font-family: system-ui, sans-serif; max-width: 800px; margin: 0 auto; padding: 2rem; line-height: 1.5; color: light-dark(#111, #eee); background: light-dark(#fff, #111); }
|
|
9
|
+
h1 { border-bottom: 2px solid light-dark(#eee, #333); padding-bottom: 0.5rem; }
|
|
10
|
+
ul { list-style: none; padding: 0; }
|
|
11
|
+
li { margin: 0.5rem 0; border: 1px solid light-dark(#ddd, #444); border-radius: 4px; }
|
|
12
|
+
a { display: block; padding: 1rem; text-decoration: none; color: light-dark(#0055aa, #44aaff); font-weight: bold; }
|
|
13
|
+
a:hover { background: light-dark(#f5f5f5, #222); }
|
|
14
|
+
.meta { font-weight: normal; color: light-dark(#666, #aaa); font-size: 0.9em; float: right; }
|
|
15
|
+
</style>
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
<h1><%= project_name %> documentation</h1>
|
|
19
|
+
<ul>
|
|
20
|
+
<% versions.each do |version| %>
|
|
21
|
+
<li>
|
|
22
|
+
<a href='<%= version.slug %>/index.html'>
|
|
23
|
+
<%= version.name %>
|
|
24
|
+
<span class='meta'>
|
|
25
|
+
<% if version.latest? %>
|
|
26
|
+
Latest
|
|
27
|
+
<% elsif version.edge? %>
|
|
28
|
+
Edge
|
|
29
|
+
<% else %>
|
|
30
|
+
Historical
|
|
31
|
+
<% end %>
|
|
32
|
+
</span>
|
|
33
|
+
</a>
|
|
34
|
+
</li>
|
|
35
|
+
<% end %>
|
|
36
|
+
</ul>
|
|
37
|
+
</body>
|
|
38
|
+
</html>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
desc "Generate SourceHut build manifests from template"
|
|
7
|
+
task :sourcehut do
|
|
8
|
+
require "erb"
|
|
9
|
+
require "yaml"
|
|
10
|
+
|
|
11
|
+
spec = Gem::Specification.load("ratatui_ruby.gemspec")
|
|
12
|
+
gem_filename = "#{spec.name}-#{spec.version}.gem"
|
|
13
|
+
|
|
14
|
+
rubies = YAML.load_file("tasks/resources/rubies.yml")
|
|
15
|
+
template = File.read("tasks/resources/build.yml.erb")
|
|
16
|
+
erb = ERB.new(template, trim_mode: "-")
|
|
17
|
+
|
|
18
|
+
FileUtils.mkdir_p ".builds"
|
|
19
|
+
|
|
20
|
+
# Remove old generated files to ensure a clean state
|
|
21
|
+
Dir.glob(".builds/*.yml").each { |f| File.delete(f) }
|
|
22
|
+
|
|
23
|
+
rubies.each do |ruby_version|
|
|
24
|
+
filename = ".builds/ruby-#{ruby_version}.yml"
|
|
25
|
+
puts "Generating #{filename}..."
|
|
26
|
+
content = erb.result_with_hash(ruby_version:, gem_filename:)
|
|
27
|
+
File.write(filename, content)
|
|
28
|
+
end
|
|
29
|
+
end
|
data/tasks/test.rake
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
require "minitest/test_task"
|
|
7
|
+
|
|
8
|
+
namespace :cargo do
|
|
9
|
+
desc "Run cargo tests"
|
|
10
|
+
task :test do
|
|
11
|
+
sh "cd ext/ratatui_ruby && cargo test"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Clear the default test task created by Minitest::TestTask if it exists
|
|
16
|
+
Rake::Task["test"].clear if Rake::Task.task_defined?("test")
|
|
17
|
+
|
|
18
|
+
desc "Run all tests (Ruby and Rust)"
|
|
19
|
+
task test: %w[test:ruby test:rust]
|
|
20
|
+
|
|
21
|
+
namespace :test do
|
|
22
|
+
desc "Run Rust tests"
|
|
23
|
+
task :rust do
|
|
24
|
+
Rake::Task["cargo:test"].invoke
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Create a specific Minitest task for Ruby tests
|
|
28
|
+
Minitest::TestTask.create(:ruby) do |t|
|
|
29
|
+
t.test_globs = ["test/**/test_*.rb", "examples/**/test_*.rb"]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
require "erb"
|
|
7
|
+
|
|
8
|
+
class IndexPage
|
|
9
|
+
def initialize(versions)
|
|
10
|
+
@versions = versions
|
|
11
|
+
|
|
12
|
+
latest_version = @versions.find { |v| v.is_a?(Tagged) }
|
|
13
|
+
latest_version.is_latest = true if latest_version
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def publish_to(path, project_name:)
|
|
17
|
+
puts "Generating index page..."
|
|
18
|
+
|
|
19
|
+
template_path = File.expand_path("../resources/index.html.erb", __dir__)
|
|
20
|
+
template = File.read(template_path)
|
|
21
|
+
|
|
22
|
+
versions = @versions
|
|
23
|
+
# project_name is used in the ERB
|
|
24
|
+
html_content = ERB.new(template).result(binding)
|
|
25
|
+
|
|
26
|
+
File.write(path, html_content)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
require "rubygems"
|
|
7
|
+
require "fileutils"
|
|
8
|
+
|
|
9
|
+
class Version
|
|
10
|
+
def self.all
|
|
11
|
+
tags = `git tag`.split.grep(/^v\d/)
|
|
12
|
+
sorted_versions = tags.map { |t| Tagged.new(t) }
|
|
13
|
+
.sort_by { |v| v.semver }
|
|
14
|
+
.reverse
|
|
15
|
+
|
|
16
|
+
[Edge.new] + sorted_versions
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def slug
|
|
20
|
+
raise NotImplementedError
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def name
|
|
24
|
+
raise NotImplementedError
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def type
|
|
28
|
+
raise NotImplementedError
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def checkout(globs:, &block)
|
|
32
|
+
raise NotImplementedError
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def latest?
|
|
36
|
+
false
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def edge?
|
|
40
|
+
false
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class Edge < Version
|
|
45
|
+
def slug
|
|
46
|
+
"main"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def name
|
|
50
|
+
"main"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def type
|
|
54
|
+
:edge
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def edge?
|
|
58
|
+
true
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def checkout(globs:, &block)
|
|
62
|
+
Dir.mktmpdir do |path|
|
|
63
|
+
# Use git ls-files for accurate source list
|
|
64
|
+
files = `git ls-files`.split("\n").select do |f|
|
|
65
|
+
globs.any? { |glob| File.fnmatch(glob, f, File::FNM_PATHNAME) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
files.each do |file|
|
|
69
|
+
dest = File.join(path, file)
|
|
70
|
+
FileUtils.mkdir_p(File.dirname(dest))
|
|
71
|
+
FileUtils.cp(file, dest)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
yield path
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class Tagged < Version
|
|
80
|
+
attr_reader :tag
|
|
81
|
+
|
|
82
|
+
def initialize(tag)
|
|
83
|
+
@tag = tag
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def slug
|
|
87
|
+
@tag
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def name
|
|
91
|
+
@tag
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def type
|
|
95
|
+
:version
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def semver
|
|
99
|
+
Gem::Version.new(@tag.sub(/^v/, ""))
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
attr_accessor :is_latest
|
|
103
|
+
|
|
104
|
+
def latest?
|
|
105
|
+
@is_latest
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def checkout(globs:, &block)
|
|
109
|
+
Dir.mktmpdir do |path|
|
|
110
|
+
system("git archive #{@tag} | tar -x -C #{path}")
|
|
111
|
+
# We could enforce globs here too, but git archive is usually sufficient.
|
|
112
|
+
FileUtils.rm_rf("#{path}/ext")
|
|
113
|
+
yield path
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
require_relative "../rdoc_config"
|
|
7
|
+
|
|
8
|
+
class VersionedDocumentation
|
|
9
|
+
def initialize(version)
|
|
10
|
+
@version = version
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def publish_to(path, project_name:, globs:, assets: [])
|
|
14
|
+
puts "Building documentation for #{@version.name}..."
|
|
15
|
+
|
|
16
|
+
absolute_path = File.absolute_path(path)
|
|
17
|
+
gemfile_path = File.absolute_path("Gemfile")
|
|
18
|
+
|
|
19
|
+
@version.checkout(globs: globs) do |source_path|
|
|
20
|
+
Dir.chdir(source_path) do
|
|
21
|
+
title = "#{project_name} #{@version.name}"
|
|
22
|
+
title = "#{project_name} (main)" if @version.edge?
|
|
23
|
+
|
|
24
|
+
# We need to expand globs relative to the source path
|
|
25
|
+
files = globs.flat_map { |glob| Dir[glob] }.uniq
|
|
26
|
+
|
|
27
|
+
system(
|
|
28
|
+
{ "BUNDLE_GEMFILE" => gemfile_path },
|
|
29
|
+
"bundle exec rdoc -o #{absolute_path} --main #{RDocConfig::MAIN} --title '#{title}' #{files.join(' ')}"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
copy_assets_to(absolute_path, assets)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def copy_assets_to(path, assets)
|
|
40
|
+
assets.each do |asset_dir|
|
|
41
|
+
if Dir.exist?(asset_dir)
|
|
42
|
+
destination = File.join(path, asset_dir)
|
|
43
|
+
FileUtils.mkdir_p(destination)
|
|
44
|
+
FileUtils.cp_r Dir["#{asset_dir}/*"], destination
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|