tint_me 1.0.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 +7 -0
- data/.rubocop_todo.yml +7 -0
- data/.serena/project.yml +68 -0
- data/.simplecov +24 -0
- data/.yardopts +6 -0
- data/AGENTS.md +60 -0
- data/CHANGELOG.md +29 -0
- data/CLAUDE.md +1 -0
- data/LICENSE.txt +21 -0
- data/README.md +175 -0
- data/RELEASING.md +202 -0
- data/Rakefile +18 -0
- data/benchmark/2025-09-08-style-caching-optimization/01_baseline_results.txt +39 -0
- data/benchmark/2025-09-08-style-caching-optimization/02_with_full_caching_results.txt +54 -0
- data/benchmark/2025-09-08-style-caching-optimization/03_prefix_only_caching_results.txt +107 -0
- data/benchmark/2025-09-08-style-caching-optimization/04_baseline_vs_optimized_analysis.txt +65 -0
- data/benchmark/2025-09-08-style-caching-optimization/05_caching_approaches_comparison.txt +59 -0
- data/benchmark/2025-09-08-style-caching-optimization/06_append_operator_results.txt +107 -0
- data/benchmark/2025-09-08-style-caching-optimization/07_string_concatenation_comparison.txt +66 -0
- data/benchmark/2025-09-08-style-caching-optimization/08_with_freeze_optimization_results.txt +107 -0
- data/benchmark/2025-09-08-style-caching-optimization/09_freeze_optimization_analysis.txt +49 -0
- data/benchmark/2025-09-08-style-caching-optimization/10_constant_access_results.txt +107 -0
- data/benchmark/2025-09-08-style-caching-optimization/11_constant_vs_cache_analysis.txt +74 -0
- data/benchmark/2025-09-08-style-caching-optimization/12_empty_prefix_analysis.txt +81 -0
- data/benchmark/2025-09-08-style-caching-optimization/13_nil_check_optimization_results.txt +107 -0
- data/benchmark/2025-09-08-style-caching-optimization/14_nil_vs_empty_check_analysis.txt +81 -0
- data/benchmark/2025-09-08-style-caching-optimization/README.md +45 -0
- data/benchmark/2025-09-08-style-caching-optimization/benchmark_script.rb +180 -0
- data/docs/agents/git-pr.md +298 -0
- data/docs/agents/languages.md +388 -0
- data/docs/agents/rubocop.md +55 -0
- data/lib/tint_me/error.rb +6 -0
- data/lib/tint_me/sgr_builder.rb +259 -0
- data/lib/tint_me/style/schema.rb +22 -0
- data/lib/tint_me/style/types.rb +50 -0
- data/lib/tint_me/style.rb +286 -0
- data/lib/tint_me/version.rb +8 -0
- data/lib/tint_me.rb +62 -0
- data/mise.toml +5 -0
- data/sig/tint_me.rbs +61 -0
- metadata +131 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 32acd1788229fa045285ea19649c993b0b4f9e59758ae92ec9a0eee630b58544
|
4
|
+
data.tar.gz: 89cfff7bc922b2fcbf876263b2a2b8dccf8762663bf15777e0b877f127d57553
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 118fa5cf809036f4169c0b7e0832fa4a90c14d92298db7f3afac16bb70bd7855200f8bed0b600369c3163a1a1a2f7d7d91c619a265a3720b030020207aeeea67
|
7
|
+
data.tar.gz: dc3e91e45584ed1c353bc28cf8d095af7a429ed0873d5aa88ea4ed2cf5e289c1e3259ac3ef24806d2147a0d2451f31dd313a3b6da4cc2aaf511cd4f538558cfe
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
|
3
|
+
# using RuboCop version 1.80.2.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
data/.serena/project.yml
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# language of the project (csharp, python, rust, java, typescript, go, cpp, or ruby)
|
2
|
+
# * For C, use cpp
|
3
|
+
# * For JavaScript, use typescript
|
4
|
+
# Special requirements:
|
5
|
+
# * csharp: Requires the presence of a .sln file in the project folder.
|
6
|
+
language: ruby
|
7
|
+
|
8
|
+
# whether to use the project's gitignore file to ignore files
|
9
|
+
# Added on 2025-04-07
|
10
|
+
ignore_all_files_in_gitignore: true
|
11
|
+
# list of additional paths to ignore
|
12
|
+
# same syntax as gitignore, so you can use * and **
|
13
|
+
# Was previously called `ignored_dirs`, please update your config if you are using that.
|
14
|
+
# Added (renamed) on 2025-04-07
|
15
|
+
ignored_paths: []
|
16
|
+
|
17
|
+
# whether the project is in read-only mode
|
18
|
+
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
|
19
|
+
# Added on 2025-04-18
|
20
|
+
read_only: false
|
21
|
+
|
22
|
+
|
23
|
+
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
|
24
|
+
# Below is the complete list of tools for convenience.
|
25
|
+
# To make sure you have the latest list of tools, and to view their descriptions,
|
26
|
+
# execute `uv run scripts/print_tool_overview.py`.
|
27
|
+
#
|
28
|
+
# * `activate_project`: Activates a project by name.
|
29
|
+
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
|
30
|
+
# * `create_text_file`: Creates/overwrites a file in the project directory.
|
31
|
+
# * `delete_lines`: Deletes a range of lines within a file.
|
32
|
+
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
|
33
|
+
# * `execute_shell_command`: Executes a shell command.
|
34
|
+
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
|
35
|
+
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
|
36
|
+
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
|
37
|
+
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
|
38
|
+
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
|
39
|
+
# * `initial_instructions`: Gets the initial instructions for the current project.
|
40
|
+
# Should only be used in settings where the system prompt cannot be set,
|
41
|
+
# e.g. in clients you have no control over, like Claude Desktop.
|
42
|
+
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
|
43
|
+
# * `insert_at_line`: Inserts content at a given line in a file.
|
44
|
+
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
|
45
|
+
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
|
46
|
+
# * `list_memories`: Lists memories in Serena's project-specific memory store.
|
47
|
+
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
|
48
|
+
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
|
49
|
+
# * `read_file`: Reads a file within the project directory.
|
50
|
+
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
|
51
|
+
# * `remove_project`: Removes a project from the Serena configuration.
|
52
|
+
# * `replace_lines`: Replaces a range of lines within a file with new content.
|
53
|
+
# * `replace_symbol_body`: Replaces the full definition of a symbol.
|
54
|
+
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
|
55
|
+
# * `search_for_pattern`: Performs a search for a pattern in the project.
|
56
|
+
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
|
57
|
+
# * `switch_modes`: Activates modes by providing a list of their names
|
58
|
+
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
|
59
|
+
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
|
60
|
+
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
|
61
|
+
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
|
62
|
+
excluded_tools: []
|
63
|
+
|
64
|
+
# initial prompt for the project. It will always be given to the LLM upon activating the project
|
65
|
+
# (contrary to the memories, which are loaded on demand).
|
66
|
+
initial_prompt: ""
|
67
|
+
|
68
|
+
project_name: "tint_me"
|
data/.simplecov
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
SimpleCov.start do
|
4
|
+
# Coverage output directory
|
5
|
+
coverage_dir "coverage"
|
6
|
+
|
7
|
+
# Minimum coverage threshold
|
8
|
+
minimum_coverage 90
|
9
|
+
|
10
|
+
# Exclude files from coverage
|
11
|
+
add_filter "/spec/"
|
12
|
+
add_filter "/vendor/"
|
13
|
+
add_filter "/bin/"
|
14
|
+
add_filter "lib/tint_me/version.rb"
|
15
|
+
|
16
|
+
# Group coverage by directory
|
17
|
+
add_group "Main", "lib/tint_me"
|
18
|
+
|
19
|
+
# Coverage formats
|
20
|
+
formatter SimpleCov::Formatter::MultiFormatter.new([
|
21
|
+
SimpleCov::Formatter::HTMLFormatter,
|
22
|
+
SimpleCov::Formatter::SimpleFormatter
|
23
|
+
])
|
24
|
+
end
|
data/.yardopts
ADDED
data/AGENTS.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# Repository Guidelines
|
2
|
+
|
3
|
+
> **Note**: This file is also accessible via the `CLAUDE.md` symlink for AI agent compatibility.
|
4
|
+
|
5
|
+
> **Important**: This document references other documentation files. Please also read:
|
6
|
+
> - `docs/agents/rubocop.md` - RuboCop fix guidelines for AI agents
|
7
|
+
> - `docs/agents/git-pr.md` - Git commit and pull request guidelines
|
8
|
+
> - `docs/agents/languages.md` - Language usage guidelines for communication and code
|
9
|
+
> - Any other documents referenced inline below
|
10
|
+
|
11
|
+
## Project Structure & Module Organization
|
12
|
+
- `lib/tint_me`: Core library. Entry point is `lib/tint_me.rb`; code is namespaced under `TIntMe` (autoloaded via Zeitwerk).
|
13
|
+
- `spec`: RSpec tests. Mirror library paths (e.g., `spec/tint_me/style_spec.rb`).
|
14
|
+
- `docs/api`: Generated YARD documentation. Do not edit by hand; use `rake doc`.
|
15
|
+
- `benchmark/*`: Performance experiments and notes.
|
16
|
+
- `bin`: Development helpers (`bin/setup`, `bin/console`).
|
17
|
+
|
18
|
+
## Build, Test, and Development Commands
|
19
|
+
- `bundle install`: Install dependencies (use a supported Ruby; see policy below and `mise.toml`).
|
20
|
+
- `rake`: Default task; runs `spec` and `rubocop`.
|
21
|
+
- `rake spec`: Run the test suite.
|
22
|
+
- `rubocop` or `rake rubocop`: Lint and style checks.
|
23
|
+
- `rake doc`: Build YARD docs into `docs/api`.
|
24
|
+
- `bin/console`: IRB with the gem loaded for quick experiments.
|
25
|
+
- `docquet regenerate-todo`: Regenerate `.rubocop_todo.yml` after lint updates; include with related code fixes.
|
26
|
+
|
27
|
+
## Communication & Languages
|
28
|
+
See `docs/agents/languages.md` for detailed language usage guidelines covering:
|
29
|
+
- AI/user chat communication languages
|
30
|
+
- Source code and documentation language requirements
|
31
|
+
- Issues/PRs language preferences
|
32
|
+
- Context-appropriate language switching
|
33
|
+
|
34
|
+
## Coding Style & Naming Conventions
|
35
|
+
- Ruby 3.x compatible; 2-space indent, `# frozen_string_literal: true` headers.
|
36
|
+
- Follow RuboCop rules (`.rubocop.yml`); fix offenses before committing.
|
37
|
+
- Files and specs use snake_case; specs live under `spec/tint_me/*_spec.rb`.
|
38
|
+
- Public API is under `TIntMe`; avoid monkey patching. Prefer immutable, composable objects (e.g., `Style` and `>>`).
|
39
|
+
- RuboCop fixes: When addressing lints, follow `docs/agents/rubocop.md` (safe autocorrect first; targeted unsafe only as needed).
|
40
|
+
|
41
|
+
## Testing Guidelines
|
42
|
+
- Framework: RSpec with `spec_helper` and SimpleCov. Maintain ≥ 90% coverage.
|
43
|
+
- Name/spec files to mirror library paths; keep examples focused and readable.
|
44
|
+
- Run `rake spec` locally; ensure `.rspec_status` is clean.
|
45
|
+
|
46
|
+
## Commit & Pull Request Guidelines
|
47
|
+
- Title format: Must start with a GitHub `:emoji:` code followed by a space, then an imperative subject. Example: `:zap: Optimize Style#call path`.
|
48
|
+
- No raw emoji: Use `:emoji:` codes only (commit hook rejects Unicode emoji).
|
49
|
+
- Exceptions: `fixup!` / `squash!` are allowed by hooks.
|
50
|
+
- Merge commits: Auto-prefixed with `:inbox_tray:` by the prepare-commit-msg hook.
|
51
|
+
- Commit body: English, explaining motivation, approach, and trade-offs.
|
52
|
+
- Include rationale and, when useful, before/after snippets or benchmarks.
|
53
|
+
- Link issues (e.g., `Fixes #123`) and update README/CHANGELOG when user-facing behavior changes.
|
54
|
+
- PRs must pass `rake` (tests + lint), include tests for changes, and keep API docs current (`rake doc` when needed).
|
55
|
+
|
56
|
+
## Security & Configuration Tips
|
57
|
+
- Supported Ruby: latest patch of the newest three minor series (e.g., 3.4.x / 3.3.x / 3.2.x). Develop locally on the oldest of these.
|
58
|
+
- Version management: Use `mise`; the repo’s `mise.toml` sets the default to the oldest supported series. Examples: `mise use -g ruby@3.2`, `mise run -e ruby@3.3 rake spec`.
|
59
|
+
- Keep runtime dependencies minimal; prefer standard library where possible.
|
60
|
+
- No network access is expected at runtime; avoid introducing it without discussion.
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
## [Unreleased]
|
2
|
+
|
3
|
+
## [1.0.0] - 2025-09-09
|
4
|
+
|
5
|
+
### Added
|
6
|
+
- Core Style class with immutable data structure using Data.define (extracted from Fasti gem)
|
7
|
+
- Support for foreground/background colors (named colors, hex values)
|
8
|
+
- Text effects: bold, faint, italic, underline (including double), overline, blink, hide, inverse
|
9
|
+
- Style composition with >> operator for layering styles
|
10
|
+
- Bold/faint mutual exclusion handling in composition
|
11
|
+
- TIntMe[] shortcut method for convenient style creation
|
12
|
+
- Style#call and Style#[] methods for applying styles to text
|
13
|
+
- Native ANSI SGR implementation with comprehensive color and effect support
|
14
|
+
- Zeitwerk autoloader with custom inflection rules for TIntMe
|
15
|
+
- Comprehensive test suite with 100% code coverage
|
16
|
+
- SimpleCov integration for coverage reporting
|
17
|
+
- YARD documentation generation with redcarpet markdown support
|
18
|
+
- RuboCop configuration with systematic violation resolution
|
19
|
+
- Rake tasks for testing, linting, and documentation generation
|
20
|
+
|
21
|
+
### Development
|
22
|
+
- Complete project setup with proper gem structure
|
23
|
+
- Git configuration with appropriate .gitignore patterns
|
24
|
+
- Bundler gem tasks integration
|
25
|
+
- RSpec test framework with progress format output
|
26
|
+
- Development dependencies: RuboCop, YARD, SimpleCov
|
27
|
+
- Continuous integration ready configuration
|
28
|
+
- Comprehensive AI agent guidelines for Git/PR operations and language usage
|
29
|
+
- Performance guidelines for style composition optimization
|
data/CLAUDE.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
AGENTS.md
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 OZAWA Sakuro
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
# :lipstick: TIntMe! :nail_care:
|
2
|
+
|
3
|
+
A Ruby library for terminal text styling with ANSI colors and effects. TIntMe! provides an elegant and composable API for applying colors, text decorations, and formatting to terminal output.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- **Rich Color Support**: Foreground and background colors with support for standard colors and hex values
|
8
|
+
- **Text Effects**: Bold, faint, italic, underline (including double), overline, blink, inverse, and concealed text
|
9
|
+
- **Style Composition**: Combine multiple styles using the `>>` operator for layered styling
|
10
|
+
- **Type Safety**: Comprehensive argument validation using dry-schema and dry-types
|
11
|
+
- **Immutable Design**: All style operations return new instances, making them safe for concurrent use
|
12
|
+
- **Zeitwerk Integration**: Automatic loading with proper module organization
|
13
|
+
- **Comprehensive API**: Both explicit `Style.new` and convenient `TIntMe[]` shortcut syntax
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem "tint_me"
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
```bash
|
26
|
+
bundle install
|
27
|
+
```
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
```bash
|
32
|
+
gem install tint_me
|
33
|
+
```
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
### Basic Styling
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
require 'tint_me'
|
41
|
+
|
42
|
+
# Create a style
|
43
|
+
red_style = TIntMe::Style.new(foreground: :red)
|
44
|
+
puts red_style.call("Hello World")
|
45
|
+
|
46
|
+
# Using the shortcut syntax
|
47
|
+
blue_style = TIntMe[foreground: :blue, bold: true]
|
48
|
+
puts blue_style["Hello World"]
|
49
|
+
```
|
50
|
+
|
51
|
+
### Color Options
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
# Standard colors
|
55
|
+
TIntMe[foreground: :red]
|
56
|
+
TIntMe[background: :yellow]
|
57
|
+
|
58
|
+
# Hex colors (with or without #)
|
59
|
+
TIntMe[foreground: "#FF0000"]
|
60
|
+
TIntMe[background: "#00FF00"]
|
61
|
+
TIntMe[foreground: "FF0000"]
|
62
|
+
```
|
63
|
+
|
64
|
+
### Text Effects
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
# Individual effects
|
68
|
+
TIntMe[bold: true]
|
69
|
+
TIntMe[faint: true] # Faint/dim text
|
70
|
+
TIntMe[italic: true]
|
71
|
+
TIntMe[underline: true]
|
72
|
+
TIntMe[underline: :double] # Double underline
|
73
|
+
TIntMe[overline: true] # Overline decoration
|
74
|
+
TIntMe[blink: true] # Blinking text
|
75
|
+
TIntMe[inverse: true] # Reverse colors
|
76
|
+
TIntMe[conceal: true] # Hidden/concealed text
|
77
|
+
|
78
|
+
# Multiple effects
|
79
|
+
TIntMe[foreground: :green, bold: true, underline: true]
|
80
|
+
```
|
81
|
+
|
82
|
+
### Style Composition
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
# Base styling
|
86
|
+
base = TIntMe[foreground: :blue]
|
87
|
+
emphasis = TIntMe[bold: true, underline: true]
|
88
|
+
|
89
|
+
# Combine styles (right-hand style takes precedence)
|
90
|
+
combined = base >> emphasis
|
91
|
+
puts combined.call("Styled text")
|
92
|
+
|
93
|
+
# Chain multiple compositions
|
94
|
+
final = base >> emphasis >> TIntMe[background: :white]
|
95
|
+
```
|
96
|
+
|
97
|
+
#### ⚡ Performance Considerations
|
98
|
+
|
99
|
+
**TIntMe is optimized for reusable styles** through SGR sequence pre-computation. The composition operator (`>>`) should be used thoughtfully:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
# ✅ RECOMMENDED: Pre-compose and reuse
|
103
|
+
ERROR_STYLE = TIntMe[foreground: :red] >> TIntMe[bold: true]
|
104
|
+
ERROR_STYLE.call("Error message") # Fast: ~4.8M operations/sec
|
105
|
+
|
106
|
+
# ❌ AVOID: Runtime composition
|
107
|
+
(TIntMe[foreground: :red] >> TIntMe[bold: true]).call("Error") # Slow: ~0.01M ops/sec
|
108
|
+
```
|
109
|
+
|
110
|
+
**Key Guidelines:**
|
111
|
+
- **Use `>>` for initialization**: Create composed styles once and reuse them
|
112
|
+
- **Avoid runtime composition**: Don't chain `>>` operators inside loops or frequently-called methods
|
113
|
+
- **For one-time styling**: Consider alternatives like `Paint` gem for better dynamic performance
|
114
|
+
- **Each `>>` operation**: Creates new Style instances and recalculates SGR sequences
|
115
|
+
|
116
|
+
**Performance Comparison:**
|
117
|
+
- Pre-computed styles: **~4.8M operations/sec** (fastest)
|
118
|
+
- Runtime composition: **~0.01M operations/sec** (246x slower)
|
119
|
+
- Direct Style.new: **~0.03M operations/sec** (71x slower)
|
120
|
+
|
121
|
+
#### 🎯 When to Use TIntMe vs Alternatives
|
122
|
+
|
123
|
+
**TIntMe excels at:**
|
124
|
+
```ruby
|
125
|
+
# Terminal UI frameworks with predefined styles
|
126
|
+
UI_STYLES = {
|
127
|
+
error: TIntMe[foreground: :red] >> TIntMe[bold: true],
|
128
|
+
success: TIntMe[foreground: :green] >> TIntMe[bold: true],
|
129
|
+
info: TIntMe[foreground: :blue] >> TIntMe[italic: true]
|
130
|
+
}
|
131
|
+
|
132
|
+
def show_error(msg)
|
133
|
+
puts UI_STYLES[:error].call(msg) # Extremely fast: ~4.8M ops/sec
|
134
|
+
end
|
135
|
+
```
|
136
|
+
|
137
|
+
**Consider alternatives for:**
|
138
|
+
```ruby
|
139
|
+
# Dynamic styling (use Paint gem instead)
|
140
|
+
texts.each { |text| Paint[text, :red, :bold] } # ~2M ops/sec
|
141
|
+
|
142
|
+
# One-time styling with readable syntax (use Rainbow gem)
|
143
|
+
puts Rainbow("Success").green.bold # ~0.5M ops/sec
|
144
|
+
|
145
|
+
# Avoid with TIntMe - creates unnecessary overhead
|
146
|
+
texts.each { |text| (red >> bold).call(text) } # Only ~0.01M ops/sec
|
147
|
+
```
|
148
|
+
|
149
|
+
**Design Philosophy:**
|
150
|
+
TIntMe is intentionally optimized for the **"define once, use many"** pattern through SGR sequence pre-computation at initialization time. The performance characteristics guide you toward the most efficient usage patterns, where a small set of predefined styles serves many styling operations.
|
151
|
+
|
152
|
+
### Method Aliases
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
style = TIntMe[foreground: :red, bold: true]
|
156
|
+
|
157
|
+
# All of these are equivalent
|
158
|
+
puts style.call("Hello")
|
159
|
+
puts style["Hello"]
|
160
|
+
puts style.("Hello") # Callable syntax
|
161
|
+
```
|
162
|
+
|
163
|
+
## Development
|
164
|
+
|
165
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
166
|
+
|
167
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
168
|
+
|
169
|
+
## Contributing
|
170
|
+
|
171
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/sakuro/tint_me.
|
172
|
+
|
173
|
+
## License
|
174
|
+
|
175
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/RELEASING.md
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
# Releasing
|
2
|
+
|
3
|
+
This document describes the release process for the tint_me gem, which is fully automated through GitHub Actions workflows.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
The release process consists of three automated workflows:
|
8
|
+
|
9
|
+
1. **Release Preparation** - Creates a release branch with version updates
|
10
|
+
2. **Release Validation** - Validates the release branch on PR creation
|
11
|
+
3. **Release Publish** - Publishes the gem after PR merge
|
12
|
+
|
13
|
+
## Prerequisites
|
14
|
+
|
15
|
+
Before initiating a release, ensure:
|
16
|
+
|
17
|
+
- [ ] All desired features and fixes are merged to `main`
|
18
|
+
- [ ] CI is passing on `main` branch
|
19
|
+
- [ ] `CHANGELOG.md` has entries under `## [Unreleased]` section
|
20
|
+
- [ ] `RUBYGEMS_API_KEY` secret is configured in repository settings
|
21
|
+
|
22
|
+
## Release Process
|
23
|
+
|
24
|
+
### Step 1: Initiate Release
|
25
|
+
|
26
|
+
1. Go to the [Actions tab](../../actions) in the GitHub repository
|
27
|
+
2. Select "Release Preparation" workflow
|
28
|
+
3. Click "Run workflow"
|
29
|
+
4. Enter the version number (e.g., `1.0.0`)
|
30
|
+
5. Click "Run workflow"
|
31
|
+
|
32
|
+
The workflow will automatically:
|
33
|
+
- Create a new branch `release-v{version}`
|
34
|
+
- Update `lib/tint_me/version.rb` with the new version
|
35
|
+
- Update `CHANGELOG.md`:
|
36
|
+
- Replace `## [Unreleased]` with `## [{version}] - {date}`
|
37
|
+
- Add a new `## [Unreleased]` section for future changes
|
38
|
+
- Create a git tag `v{version}` on the release branch
|
39
|
+
- Push the branch and tag to GitHub
|
40
|
+
- Create a Pull Request to `main`
|
41
|
+
|
42
|
+
### Step 2: Review and Merge
|
43
|
+
|
44
|
+
Once the PR is created, the Release Validation workflow automatically:
|
45
|
+
- Validates version format (must be `x.y.z`)
|
46
|
+
- Verifies version consistency between branch name and `version.rb`
|
47
|
+
- Checks that the git tag doesn't already exist (or can be updated)
|
48
|
+
- Confirms the version isn't already published on RubyGems
|
49
|
+
- Verifies `RUBYGEMS_API_KEY` secret is configured
|
50
|
+
- Validates `CHANGELOG.md` has an entry for this version
|
51
|
+
|
52
|
+
**Note**: Quality checks (tests and RuboCop) are handled by the CI workflow to avoid duplication.
|
53
|
+
|
54
|
+
**Important**: If you push additional commits to the release PR (e.g., bug fixes, workflow updates, documentation changes):
|
55
|
+
- The validation workflow automatically moves the release tag to the latest commit
|
56
|
+
- This ensures the tag always points to the final reviewed code
|
57
|
+
- No manual intervention required
|
58
|
+
- All changes will be included in the final release
|
59
|
+
|
60
|
+
If all checks pass, merge the PR.
|
61
|
+
|
62
|
+
### Step 3: Automatic Publishing
|
63
|
+
|
64
|
+
After the PR is merged, the Release Publish workflow automatically:
|
65
|
+
- Checks out the exact git tag created on the release branch
|
66
|
+
- Builds the gem from the tagged commit
|
67
|
+
- Publishes the gem to RubyGems
|
68
|
+
- Creates a GitHub Release with:
|
69
|
+
- Release notes extracted from `CHANGELOG.md`
|
70
|
+
- The built gem file as an attachment
|
71
|
+
- Cleans up the release branch
|
72
|
+
|
73
|
+
## Workflow Architecture
|
74
|
+
|
75
|
+
### Key Design Decisions
|
76
|
+
|
77
|
+
#### Tag-Based Deployment
|
78
|
+
|
79
|
+
The release workflow uses a **tag-based deployment strategy** to ensure the released gem contains exactly the code that was reviewed and approved in the release PR, without any subsequent changes from `main`.
|
80
|
+
|
81
|
+
```mermaid
|
82
|
+
graph LR
|
83
|
+
A[main branch] -->|Create release branch| B[release-v1.0.0]
|
84
|
+
B -->|Update version & tag| C[Tagged: v1.0.0]
|
85
|
+
B -->|Create PR| D[Pull Request]
|
86
|
+
A -->|Other PRs merged| E[main with new commits]
|
87
|
+
D -->|Merge PR| E
|
88
|
+
C -->|Checkout tag| F[Build & Publish gem]
|
89
|
+
F -->|Contains only| G[Code from release branch]
|
90
|
+
```
|
91
|
+
|
92
|
+
#### Automatic Tag Movement
|
93
|
+
|
94
|
+
When additional commits are pushed to a release PR, the tag automatically moves to the latest commit:
|
95
|
+
|
96
|
+
```mermaid
|
97
|
+
graph LR
|
98
|
+
A[Initial commit] -->|Tag v1.0.0| B[Tagged commit]
|
99
|
+
B -->|Fix typo| C[New commit]
|
100
|
+
C -->|Auto-move tag| D[Tag v1.0.0 on latest]
|
101
|
+
D -->|Final review| E[Ready to merge]
|
102
|
+
```
|
103
|
+
|
104
|
+
This ensures:
|
105
|
+
- The tag always points to the final reviewed code
|
106
|
+
- No manual tag management required
|
107
|
+
- The published gem matches exactly what was approved
|
108
|
+
|
109
|
+
## Manual Release (Emergency Only)
|
110
|
+
|
111
|
+
If automation fails, you can release manually using our tag-based strategy:
|
112
|
+
|
113
|
+
```bash
|
114
|
+
# 1. Create release branch
|
115
|
+
git checkout -b release-v1.0.0
|
116
|
+
|
117
|
+
# 2. Update version
|
118
|
+
vim lib/tint_me/version.rb
|
119
|
+
|
120
|
+
# 3. Update CHANGELOG (move content from [Unreleased] to [1.0.0])
|
121
|
+
vim CHANGELOG.md
|
122
|
+
|
123
|
+
# 4. Commit changes and create tag
|
124
|
+
git add -A
|
125
|
+
git commit -m ":bookmark: Release v1.0.0"
|
126
|
+
git tag -a v1.0.0 -m "Release v1.0.0"
|
127
|
+
|
128
|
+
# 5. Push release branch and tag
|
129
|
+
git push origin release-v1.0.0
|
130
|
+
git push origin v1.0.0
|
131
|
+
|
132
|
+
# 6. Create PR and merge after review
|
133
|
+
gh pr create --title "Release v1.0.0" --body "Release v1.0.0"
|
134
|
+
# (Review and merge PR)
|
135
|
+
|
136
|
+
# 7. Checkout the tag and build gem
|
137
|
+
git checkout v1.0.0
|
138
|
+
bundle exec rake build
|
139
|
+
|
140
|
+
# 8. Push to RubyGems
|
141
|
+
gem push pkg/tint_me-1.0.0.gem
|
142
|
+
|
143
|
+
# 9. Create GitHub release
|
144
|
+
gh release create v1.0.0 --title "tint_me v1.0.0" --generate-notes pkg/tint_me-1.0.0.gem
|
145
|
+
```
|
146
|
+
|
147
|
+
## Troubleshooting
|
148
|
+
|
149
|
+
### Release Validation Fails
|
150
|
+
|
151
|
+
**Version already exists on RubyGems**
|
152
|
+
- Solution: Increment the version number and try again
|
153
|
+
|
154
|
+
**RUBYGEMS_API_KEY not configured**
|
155
|
+
1. Generate an API key at https://rubygems.org/profile/edit
|
156
|
+
2. Go to repository Settings → Secrets and variables → Actions
|
157
|
+
3. Add new secret: `RUBYGEMS_API_KEY` with your API key
|
158
|
+
|
159
|
+
**Tests or RuboCop failing**
|
160
|
+
- These are checked by the CI workflow, not in release validation
|
161
|
+
- Fix the issues on the release branch or `main` as appropriate
|
162
|
+
- Release validation focuses on version consistency and format
|
163
|
+
|
164
|
+
### Release Publish Fails
|
165
|
+
|
166
|
+
**Tag not found**
|
167
|
+
- The release preparation workflow may have failed to create the tag
|
168
|
+
- Check if the tag exists: `git tag | grep v1.0.0`
|
169
|
+
- If missing, the release validation workflow will recreate it on PR updates
|
170
|
+
- For manual fix: create tag on release branch with `git tag -a v1.0.0 -m "Release v1.0.0"`
|
171
|
+
|
172
|
+
**Gem push fails**
|
173
|
+
- Verify RubyGems API key is valid
|
174
|
+
- Check if you have push permissions for the gem
|
175
|
+
- Ensure the version doesn't already exist on RubyGems
|
176
|
+
|
177
|
+
## Version Numbering
|
178
|
+
|
179
|
+
Follow [Semantic Versioning](https://semver.org/):
|
180
|
+
|
181
|
+
- **MAJOR** version for incompatible API changes
|
182
|
+
- **MINOR** version for backwards-compatible functionality additions
|
183
|
+
- **PATCH** version for backwards-compatible bug fixes
|
184
|
+
|
185
|
+
Examples:
|
186
|
+
- `0.1.0` → `0.1.1`: Bug fixes only
|
187
|
+
- `0.1.1` → `0.2.0`: New features added
|
188
|
+
- `0.2.0` → `1.0.0`: Breaking changes or stable release
|
189
|
+
|
190
|
+
## Workflow Files
|
191
|
+
|
192
|
+
The release automation is implemented in:
|
193
|
+
|
194
|
+
- `.github/workflows/release-preparation.yml` - Creates release branch and PR
|
195
|
+
- `.github/workflows/release-validation.yml` - Validates release PR
|
196
|
+
- `.github/workflows/release-publish.yml` - Publishes gem after merge
|
197
|
+
|
198
|
+
## Security Notes
|
199
|
+
|
200
|
+
- The `RUBYGEMS_API_KEY` secret is only accessible to workflows running on the default branch
|
201
|
+
- Release branches are automatically deleted after successful release
|
202
|
+
- All releases are tagged for audit trail and rollback capability
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
|
5
|
+
require "rspec/core/rake_task"
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
|
+
|
8
|
+
require "rubocop/rake_task"
|
9
|
+
RuboCop::RakeTask.new
|
10
|
+
|
11
|
+
require "yard"
|
12
|
+
YARD::Rake::YardocTask.new(:doc)
|
13
|
+
|
14
|
+
require "rake/clean"
|
15
|
+
CLEAN.include("coverage", ".yardoc", "docs/api", ".rspec_status")
|
16
|
+
CLOBBER.include("pkg")
|
17
|
+
|
18
|
+
task default: %i[spec rubocop]
|
@@ -0,0 +1,39 @@
|
|
1
|
+
BASELINE BENCHMARK RESULTS (Before Optimization)
|
2
|
+
=================================================
|
3
|
+
Date: 2025-09-08
|
4
|
+
|
5
|
+
## Performance Summary
|
6
|
+
|
7
|
+
### Style Application (operations/second)
|
8
|
+
- Simple style: ~766k ops/s (1.3μs per call)
|
9
|
+
- Complex style: ~437k ops/s (2.3μs per call)
|
10
|
+
- Hex color style: ~266k ops/s (3.8μs per call)
|
11
|
+
|
12
|
+
### Repeated Application (1000x)
|
13
|
+
- Simple style: 781 ops/s (1.28ms total)
|
14
|
+
- Complex style: 433 ops/s (2.31ms total)
|
15
|
+
- Hex style: 265 ops/s (3.78ms total)
|
16
|
+
|
17
|
+
### Memory Usage
|
18
|
+
- Simple style creation: 5.8KB, 52 objects
|
19
|
+
- Complex style creation: 6.6KB, 66 objects
|
20
|
+
- Simple style application (first): 45KB, 466 objects
|
21
|
+
- Complex style application (subsequent): 1.1KB, 14 objects
|
22
|
+
|
23
|
+
### Object Allocations (100 applications)
|
24
|
+
- Simple style: 703 objects
|
25
|
+
- Complex style: 1203 objects
|
26
|
+
|
27
|
+
## Key Observations
|
28
|
+
|
29
|
+
1. **Performance overhead**: Each call recalculates SGR sequences
|
30
|
+
2. **Object allocations**: Significant allocations on every call
|
31
|
+
3. **Hex colors slower**: RGB conversion adds ~45% overhead
|
32
|
+
4. **Complex styles slower**: More parameters = more processing
|
33
|
+
|
34
|
+
## Expected Improvements with Caching
|
35
|
+
|
36
|
+
- Reduced object allocations per call
|
37
|
+
- Consistent performance regardless of style complexity
|
38
|
+
- Minimal overhead for repeated applications
|
39
|
+
- Trade-off: Slightly higher memory per Style instance
|