rapicco 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f75b483f11ab21e49743716cf5c8e41d66d906e59a06a6cd3d91e7a68951f5da
4
- data.tar.gz: dfdd6c4403ca2cd7d652b1a4bdf13512919f4142c8e5836279142462583734fa
3
+ metadata.gz: c4e290b0008061b7bba5f6e6bcb36dc1682739d58ee216b3204e3e0f207b48ab
4
+ data.tar.gz: e1a1d04f0ad5d35ec559c10a42b44237bd5159b25a865c4645810b22d756177d
5
5
  SHA512:
6
- metadata.gz: 995b0755fb6634c30e0183b51cd7638da3fb1b6ba0f32eb56ddd4df030710fcae9c8da188a4ce698fcd7c80b10657edd6819d3cfd4e84ed093345f1f05a8885f
7
- data.tar.gz: f277684e2d09d4446793ccbaffee37175cd2109838006a1d5d95d4c3a41ba510ff802101303975a9307a14208f19c30b6772534af3663639f422c8c5433c3242
6
+ metadata.gz: 359e7e4ad563e7992cc1fbf65d3e294c032255880d1bc1cd342f024360381da2aa6a96259f86ae5d0e192db5cf98d7964fee9b031da80915640dc0890df403b5
7
+ data.tar.gz: 937be7c318d35440cfe516104a5249c43393f6c2ff975968cf7820371787dabbc7c7cd7dc92ac8e34c4c6e6a60178c7883f2d5ed2c62d62ee1b4b6441d0fa194
data/CHANGELOG.md ADDED
@@ -0,0 +1,30 @@
1
+ # CHANGELOG
2
+
3
+ ## [0.2.0] - 2025-11-11
4
+
5
+ ### Added
6
+ - New `rapicco new` command to create presentation project templates
7
+ - PDF up-to-date check: Skip PDF generation if both slide.md and config.yaml are older than the target PDF
8
+ - Configuration validation for gem creation with detailed error messages
9
+ - README.md validation to ensure proper documentation before gem creation
10
+ - Support for `--cols` and `--rows` options in PDF generation
11
+ - New constants file (lib/rapicco/constants.rb) for centralized configuration
12
+ - GitHub Actions CI workflow for automated testing
13
+ - Comprehensive test suite using test-unit framework
14
+ - Rakefile for running tests
15
+
16
+ ### Changed
17
+ - Renamed configuration file from `config.yml` to `config.yaml`
18
+ - Switched test framework from RSpec to test-unit
19
+ - Required Ruby version updated from >= 3.0.0 to >= 3.2.0
20
+ - Enhanced README.md with detailed usage instructions and examples
21
+ - Improved error handling in PDF PageCapturer with better timeout management
22
+ - Updated LICENSE copyright owner from PicoRuby to HASUMI Hitoshi
23
+ - Enabled `gem push` command for publishing to RubyGems.org
24
+
25
+ ### Removed
26
+ - Removed `ffi` dependency from gemspec and Gemfile
27
+ - Removed unused exported_pages directory
28
+
29
+ ## [0.1.0] - 2025-11-10
30
+ - The first release
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 PicoRuby
3
+ Copyright (c) 2025 HASUMI Hitoshi
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Rapicco
2
2
 
3
+ [![Test](https://github.com/picoruby/rapicco/actions/workflows/test.yml/badge.svg)](https://github.com/picoruby/rapicco/actions/workflows/test.yml)
4
+
3
5
  A wrapper tool of PicoRuby Rapicco terminal-based presentation.
4
6
 
5
7
  ## Overview
@@ -9,9 +11,10 @@ It also converts Rapicco presentations into PDF documents capturing the ANSI te
9
11
 
10
12
  ## Requirements
11
13
 
12
- - Ruby 3.0 or later
14
+ - Ruby (you can see supported versions in [rapicco.gemspec](rapicco.gemspec))
13
15
  - Cairo graphics library
14
16
  - PicoRuby with Rapicco installed
17
+ - `export PICORUBY_PATH=path/to/picoruby` needs to be set
15
18
 
16
19
  ## Installation
17
20
 
@@ -19,13 +22,71 @@ It also converts Rapicco presentations into PDF documents capturing the ANSI te
19
22
  gem install rapicco
20
23
  ```
21
24
 
22
- Or add to your Gemfile:
25
+ ## Usage
26
+
27
+ ### Create a presentation project (recommended)
23
28
 
24
- ```ruby
25
- gem 'rapicco'
29
+ 1. Create a new presentation project along with `mkdir`:
30
+
31
+ ```bash
32
+ rapicco new my-presentation
33
+ cd my-presentation
26
34
  ```
27
35
 
28
- ## Usage
36
+ Or in existing directory,
37
+
38
+ ```bash
39
+ cd my-presentation
40
+ rapicco new .
41
+ ```
42
+
43
+ > [!WARNING]
44
+ > The above command will override existing files.
45
+
46
+ This generates:
47
+ - `Gemfile` with rapicco gem
48
+ - `Rakefile` with presentation tasks
49
+ - `slide.md` template
50
+ - `config.yaml` configuration
51
+ - `README.md` template
52
+ - `.gitignore`
53
+
54
+ 2. Install dependencies:
55
+
56
+ ```bash
57
+ bundle install
58
+ ```
59
+
60
+ 3. Use rake tasks to manage your presentation:
61
+
62
+ ```bash
63
+ bundle exec rake -T
64
+ ```
65
+
66
+ Available tasks:
67
+ ```
68
+ rake gem # Create gem package
69
+ rake pdf # Generate PDF
70
+ rake publish # Publish gem to RubyGems.org
71
+ rake run # Run presentation
72
+ ```
73
+
74
+ **Show presentation:**
75
+ ```bash
76
+ bundle exec rake run
77
+ ```
78
+
79
+ **Generate PDF:**
80
+ ```bash
81
+ bundle exec rake pdf
82
+ ```
83
+
84
+ **Create gem package:**
85
+ ```bash
86
+ bundle exec rake gem
87
+ ```
88
+
89
+ ### Using CLI directly
29
90
 
30
91
  **Show presentation:**
31
92
  ```bash
@@ -0,0 +1,5 @@
1
+ module Rapicco
2
+ REQUIRED_RUBY_VERSION = '>= 3.2.0'
3
+ PDF_DEFAULT_TITLE = 'Slide Title'
4
+ PDF_DEFAULT_DESCRIPTION = 'Your slide description here.'
5
+ end
@@ -1,5 +1,6 @@
1
1
  require 'fileutils'
2
2
  require 'yaml'
3
+ require_relative 'constants'
3
4
 
4
5
  module Rapicco
5
6
  class Installer
@@ -69,18 +70,18 @@ module Rapicco
69
70
  ---
70
71
  duration: 300
71
72
  sprite: hasumikin
72
- title_font: terminus_8x16
73
- font: terminus_6x12
73
+ title_font: shinonome_go16
74
+ font: shinonome_go12
74
75
  bold_color: red
75
76
  align: center
76
- line_margin: 1
77
+ line_margin: 3
77
78
  code_indent: 2
78
79
  ---
79
80
 
80
- # Title Slide
81
+ # #{Rapicco::PDF_DEFAULT_TITLE}
81
82
  {align=center, scale=2}
82
83
 
83
- Your presentation title
84
+ #{Rapicco::PDF_DEFAULT_DESCRIPTION}
84
85
 
85
86
  # Introduction
86
87
 
@@ -100,6 +101,8 @@ module Rapicco
100
101
  config = {
101
102
  'id' => nil,
102
103
  'base_name' => nil,
104
+ 'description' => nil,
105
+ 'presentation_date' => nil,
103
106
  'tags' => [],
104
107
  'version' => nil,
105
108
  'licenses' => [],
@@ -107,10 +110,14 @@ module Rapicco
107
110
  'name' => nil,
108
111
  'email' => nil,
109
112
  'rubygems_user' => nil
113
+ },
114
+ 'pdf' => {
115
+ 'cols' => 350,
116
+ 'rows' => 196
110
117
  }
111
118
  }
112
119
 
113
- File.write(File.join(target_dir, 'config.yml'), YAML.dump(config))
120
+ File.write(File.join(target_dir, 'config.yaml'), YAML.dump(config))
114
121
  end
115
122
 
116
123
  def create_readme(target_dir)
@@ -119,13 +126,11 @@ module Rapicco
119
126
 
120
127
  Your slide description here.
121
128
 
122
- ## Author
129
+ ## Authors
123
130
 
124
131
  Your Name
125
132
 
126
133
  ## License
127
-
128
- CC BY-SA 4.0
129
134
  README
130
135
 
131
136
  File.write(File.join(target_dir, 'README.md'), content)
@@ -76,11 +76,19 @@ module Rapicco
76
76
  begin
77
77
  Timeout.timeout(timeout) do
78
78
  loop do
79
- output << stdout.read_nonblock(10000)
79
+ begin
80
+ chunk = stdout.read_nonblock(10000)
81
+ output << chunk
82
+ rescue IO::WaitReadable
83
+ if IO.select([stdout], nil, nil, 1.0)
84
+ retry
85
+ else
86
+ break
87
+ end
88
+ end
80
89
  end
81
90
  end
82
91
  rescue Timeout::Error
83
- rescue IO::WaitReadable
84
92
  rescue EOFError, Errno::EIO
85
93
  return nil
86
94
  end
@@ -1,15 +1,76 @@
1
1
  require 'fileutils'
2
2
  require 'rubygems/package'
3
+ require_relative 'constants'
3
4
 
4
5
  module Rapicco
5
6
  class SlideGemBuilder
7
+ def self.validate_config(config)
8
+ errors = []
9
+
10
+ # Required fields
11
+ errors << "config.yaml: 'id' is required" if config['id'].nil? || config['id'].empty?
12
+ errors << "config.yaml: 'base_name' is required" if config['base_name'].nil? || config['base_name'].empty?
13
+ errors << "config.yaml: 'version' is required" if config['version'].nil? || config['version'].empty?
14
+ errors << "config.yaml: 'description' is required" if config['description'].nil? || config['description'].empty?
15
+ errors << "config.yaml: 'presentation_date' is required" if config['presentation_date'].nil? || config['presentation_date'].empty?
16
+
17
+ # Licenses
18
+ if config['licenses'].nil? || config['licenses'].empty?
19
+ errors << "config.yaml: 'licenses' must have at least one license"
20
+ end
21
+
22
+ # Author fields
23
+ if config['author'].nil?
24
+ errors << "config.yaml: 'author' section is required"
25
+ else
26
+ errors << "config.yaml: 'author.name' is required" if config['author']['name'].nil? || config['author']['name'].empty?
27
+ errors << "config.yaml: 'author.rubygems_user' is required" if config['author']['rubygems_user'].nil? || config['author']['rubygems_user'].empty?
28
+ end
29
+
30
+ unless errors.empty?
31
+ raise <<~ERROR
32
+ Configuration validation failed:
33
+
34
+ #{errors.map { |e| " - #{e}" }.join("\n")}
35
+
36
+ Please edit config.yaml and fill in all required fields.
37
+ ERROR
38
+ end
39
+ end
40
+
41
+ # raise error if README.md is missing or malformed
42
+ # checklist:
43
+ # - README.md exists
44
+ # - README.md has a title (first line starts with '# ')
45
+ # - The title is not identical to Rapicco::PDF_DEFAULT_TITLE
46
+ def self.validate_readme
47
+ unless File.file?('README.md')
48
+ raise "README.md is missing. Please provide a README.md file."
49
+ end
50
+ File.open('README.md', 'r') do |file|
51
+ lines = file.readlines.map(&:chomp)
52
+ if lines.empty?
53
+ raise "README.md is empty. Please provide a valid README.md file."
54
+ end
55
+ title_line = lines.find { |line| line.start_with?('# ') }
56
+ if title_line.nil?
57
+ raise "README.md is missing a title. The first line should start with '# '."
58
+ end
59
+ title = title_line.sub('# ', '').strip
60
+ if title.empty?
61
+ raise "README.md has an empty title. Please provide a valid title."
62
+ end
63
+ if title == Rapicco::PDF_DEFAULT_TITLE
64
+ raise "README.md title is the default placeholder. Please provide a meaningful title."
65
+ end
66
+ end
67
+ end
68
+
6
69
  def initialize(config)
7
70
  @config = config
8
71
  end
9
72
 
10
73
  def build
11
- validate_config
12
-
13
74
  FileUtils.mkdir_p('pkg')
14
75
 
15
76
  spec = build_gemspec
@@ -27,54 +88,23 @@ module Rapicco
27
88
 
28
89
  private
29
90
 
30
- def validate_config
31
- errors = []
32
-
33
- # Required fields
34
- errors << "config.yml: 'id' is required" if @config['id'].nil? || @config['id'].empty?
35
- errors << "config.yml: 'base_name' is required" if @config['base_name'].nil? || @config['base_name'].empty?
36
- errors << "config.yml: 'version' is required" if @config['version'].nil? || @config['version'].empty?
37
-
38
- # Licenses
39
- if @config['licenses'].nil? || @config['licenses'].empty?
40
- errors << "config.yml: 'licenses' must have at least one license"
41
- end
42
-
43
- # Author fields
44
- if @config['author'].nil?
45
- errors << "config.yml: 'author' section is required"
46
- else
47
- errors << "config.yml: 'author.name' is required" if @config['author']['name'].nil? || @config['author']['name'].empty?
48
- errors << "config.yml: 'author.email' is required" if @config['author']['email'].nil? || @config['author']['email'].empty?
49
- errors << "config.yml: 'author.rubygems_user' is required" if @config['author']['rubygems_user'].nil? || @config['author']['rubygems_user'].empty?
50
- end
51
-
52
- unless errors.empty?
53
- raise <<~ERROR
54
- Configuration validation failed:
55
-
56
- #{errors.map { |e| " - #{e}" }.join("\n")}
57
-
58
- Please edit config.yml and fill in all required fields.
59
- ERROR
60
- end
61
- end
62
-
63
91
  def build_gemspec
64
92
  config = @config
65
93
  slide_file = find_slide_file
66
94
  pdf_file = "pdf/#{config['id']}-#{config['base_name']}.pdf"
67
95
 
68
96
  Gem::Specification.new do |spec|
97
+ spec.required_ruby_version = Rapicco::REQUIRED_RUBY_VERSION
98
+
69
99
  spec.name = "rabbit-slide-#{config['author']['rubygems_user']}-#{config['id']}"
70
100
  spec.version = config['version']
71
101
  spec.authors = [config['author']['name']]
72
102
  spec.email = [config['author']['email']]
73
103
 
74
104
  spec.summary = "Rapicco slide: #{config['id']}"
75
- spec.description = read_description
105
+ spec.description = "#{config['description']}"
76
106
  spec.homepage = "https://slide.rabbit-shocker.org/authors/#{config['author']['rubygems_user']}/#{config['id']}/"
77
- spec.license = config['licenses'].first
107
+ spec.licenses = config['licenses']
78
108
 
79
109
  spec.metadata = {
80
110
  "rapicco.slide.id" => config['id'],
@@ -85,7 +115,7 @@ module Rapicco
85
115
  spec.files = [
86
116
  slide_file,
87
117
  pdf_file,
88
- 'config.yml',
118
+ 'config.yaml',
89
119
  'Rakefile',
90
120
  'README.md'
91
121
  ].select { |f| File.exist?(f) }
@@ -103,30 +133,5 @@ module Rapicco
103
133
  end
104
134
  end
105
135
 
106
- def read_description
107
- return @config['id'] unless File.exist?('README.md')
108
-
109
- readme = File.read('README.md')
110
- # Extract first paragraph after title
111
- lines = readme.lines
112
- description_lines = []
113
- found_title = false
114
-
115
- lines.each do |line|
116
- if line.start_with?('#')
117
- found_title = true
118
- next
119
- end
120
-
121
- next if line.strip.empty? && !found_title
122
-
123
- if found_title
124
- break if line.strip.empty? && !description_lines.empty?
125
- description_lines << line.strip
126
- end
127
- end
128
-
129
- description_lines.join(' ').strip[0..200]
130
- end
131
136
  end
132
137
  end
@@ -15,10 +15,10 @@ module Rapicco
15
15
  private
16
16
 
17
17
  def load_config
18
- unless File.exist?('config.yml')
19
- raise "config.yml not found. Please create it first."
18
+ unless File.exist?('config.yaml')
19
+ raise "config.yaml not found. Please create it first."
20
20
  end
21
- YAML.load_file('config.yml')
21
+ YAML.load_file('config.yaml')
22
22
  end
23
23
 
24
24
  def define_tasks
@@ -37,12 +37,35 @@ module Rapicco
37
37
  pdf_name = "#{@config['id']}-#{@config['base_name']}.pdf"
38
38
  pdf_path = File.join(pdf_dir, pdf_name)
39
39
 
40
- sh "rapicco -p -o #{pdf_path} #{slide_file}"
40
+ # Check if PDF is up to date
41
+ if File.exist?(pdf_path)
42
+ pdf_mtime = File.mtime(pdf_path)
43
+ slide_mtime = File.mtime(slide_file)
44
+ config_mtime = File.mtime('config.yaml')
45
+
46
+ if slide_mtime < pdf_mtime && config_mtime < pdf_mtime
47
+ puts "PDF is up to date: #{pdf_path}"
48
+ next
49
+ end
50
+ end
51
+
52
+ cols = @config.dig('pdf', 'cols') || 350
53
+ rows = @config.dig('pdf', 'rows') || 196
54
+
55
+ sh "rapicco -p -o #{pdf_path} #{slide_file} --cols #{cols} --rows #{rows}"
41
56
  puts "PDF created: #{pdf_path}"
42
57
  end
43
58
 
59
+ task :validate_config do
60
+ Rapicco::SlideGemBuilder.validate_config(@config)
61
+ end
62
+
63
+ task :validate_readme do
64
+ Rapicco::SlideGemBuilder.validate_readme
65
+ end
66
+
44
67
  desc "Create gem package"
45
- task gem: :pdf do
68
+ task gem: [:validate_config, :validate_readme, :pdf] do
46
69
  builder = Rapicco::SlideGemBuilder.new(@config)
47
70
  gem_file = builder.build
48
71
  puts "Gem created: #{gem_file}"
@@ -50,9 +73,8 @@ module Rapicco
50
73
 
51
74
  desc "Publish gem to RubyGems.org"
52
75
  task publish: :gem do
53
- puts "Publishing gem is currently disabled."
54
- #gem_file = gem_filename
55
- #sh "gem push #{gem_file}"
76
+ gem_file = gem_filename
77
+ sh "gem push #{gem_file}"
56
78
  end
57
79
  end
58
80
 
@@ -1,3 +1,3 @@
1
1
  module Rapicco
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rapicco
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - HASUMI Hitoshi
@@ -23,20 +23,6 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '1.17'
26
- - !ruby/object:Gem::Dependency
27
- name: ffi
28
- requirement: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - "~>"
31
- - !ruby/object:Gem::Version
32
- version: '1.15'
33
- type: :runtime
34
- prerelease: false
35
- version_requirements: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '1.15'
40
26
  - !ruby/object:Gem::Dependency
41
27
  name: rake
42
28
  requirement: !ruby/object:Gem::Requirement
@@ -58,10 +44,12 @@ executables:
58
44
  extensions: []
59
45
  extra_rdoc_files: []
60
46
  files:
47
+ - CHANGELOG.md
61
48
  - LICENSE
62
49
  - README.md
63
50
  - bin/rapicco
64
51
  - lib/rapicco.rb
52
+ - lib/rapicco/constants.rb
65
53
  - lib/rapicco/installer.rb
66
54
  - lib/rapicco/pdf.rb
67
55
  - lib/rapicco/pdf/ansi_parser.rb
@@ -82,7 +70,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
82
70
  requirements:
83
71
  - - ">="
84
72
  - !ruby/object:Gem::Version
85
- version: 3.0.0
73
+ version: 3.2.0
86
74
  required_rubygems_version: !ruby/object:Gem::Requirement
87
75
  requirements:
88
76
  - - ">="