diff --git a/.gitignore b/.gitignore index 7b72b37..3a3b7bf 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,6 @@ .rspec_status # Personal, private notes kept locally and excluded from the repo -# (e.g. the development diary "how-i-created-my-first-ruby-gem-with-kiro.md"). /private-notes/ # Built gem artifacts @@ -20,3 +19,13 @@ # Gems should not commit their lockfile (Bundler resolves it per environment). # Committing it here pinned Bundler 4.x, which breaks CI on Ruby < 3.2. /Gemfile.lock + +# Local example consumer project: Bundler resolves its lockfile per environment +# (same reasoning as the root Gemfile.lock above), so it is not committed. +/examples/local-consumer/Gemfile.lock +/examples/local-consumer/.bundle/ +graphify-out/ +.graphify-cache/ + +# Custom File +hermes-run.sh diff --git a/.kiro/steering/structure.md b/.kiro/steering/structure.md index 9c97b19..5c07e5d 100644 --- a/.kiro/steering/structure.md +++ b/.kiro/steering/structure.md @@ -3,7 +3,7 @@ ## Directory layout ``` -password_forge/ +password-forge-ruby-gem/ # repo root ├── lib/ │ ├── password_forge.rb # Entry point: requires all components │ └── password_forge/ @@ -19,7 +19,16 @@ password_forge/ │ ├── charset_spec.rb │ ├── validation_spec.rb │ └── generator_spec.rb +├── examples/ # Runnable local demos (not packaged) +│ ├── smoke_test.rb # Loads the gem from source and prints samples +│ └── local-consumer/ # Mini "external" project using the gem via path: +│ ├── Gemfile +│ └── run.rb +├── docs/ # Extended documentation (not packaged) +│ ├── README.md # Docs index +│ └── local-testing.md # How to try the gem locally ├── sig/ # RBS type signatures +├── bin/ # setup + console helper scripts ├── .github/workflows/ │ ├── main.yml # CI: RSpec matrix + RuboCop │ └── release.yml # Trusted Publishing on v* tags @@ -33,6 +42,9 @@ password_forge/ └── LICENSE.txt ``` +`Gemfile.lock` is generated per environment and git-ignored (see Tech), so it is +not part of the tracked tree above. + ## Architecture The internal design mirrors the original C# separation of concerns while diff --git a/.kiro/steering/tech.md b/.kiro/steering/tech.md index 62760b3..816e01f 100644 --- a/.kiro/steering/tech.md +++ b/.kiro/steering/tech.md @@ -12,7 +12,7 @@ The gem has **no runtime dependencies**. ## Common commands -Run from the `password_forge/` directory: +Run from the repository root (`password-forge-ruby-gem/`): ```bash bundle install # install development dependencies diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cc0691..e6cb866 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.2] - 2026-09-05 + +### Added + +- `examples/` folder with runnable local demos: `smoke_test.rb` (loads the gem + from source and prints sample passwords) and `local-consumer/` (a minimal + external project that depends on the gem via a `path:` reference). +- `docs/` folder with a documentation index (`docs/README.md`) and a + local-testing guide (`docs/local-testing.md`). +- "Trying it locally" section in the README linking to the examples and docs. + +### Changed + +- Excluded `examples/` and `docs/` from the packaged gem. + +### Fixed + +- Corrected the directory tree and command paths in the Kiro steering docs + (`structure.md`, `tech.md`), which still referenced a nested `password_forge/` + root left over from an older project layout. + ## [0.0.1] - 2026-09-05 ### Added @@ -22,5 +43,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `PasswordForge.generate` convenience wrapper. - RSpec test suite, RuboCop configuration and README. -[Unreleased]: https://github.com/devandreacarratta/password-forge-ruby-gem/compare/v0.0.1...HEAD +[Unreleased]: https://github.com/devandreacarratta/password-forge-ruby-gem/compare/v0.0.2...HEAD +[0.0.2]: https://github.com/devandreacarratta/password-forge-ruby-gem/compare/v0.0.1...v0.0.2 [0.0.1]: https://github.com/devandreacarratta/password-forge-ruby-gem/releases/tag/v0.0.1 diff --git a/README.md b/README.md index 5ecf6a3..8300b26 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,18 @@ categories are enabled by default, and a clear error is raised if you disable every one of them. Randomness is provided by Ruby's `SecureRandom`. This gem is also a showcase for building and shipping a Ruby gem the **Kiro -way**: it ships with Kiro skills, project steering, hooks and an MCP server (see -the [Roadmap](#roadmap)). +way**. Kiro skills, project steering, hooks and an MCP server are being added +incrementally (see the [Roadmap](#roadmap)). ## Installation -Install the gem and add it to the application's Gemfile by executing: +> **Not on RubyGems yet.** The first public release is planned for 0.1.0 (see +> the [Roadmap](#roadmap)). Until then, see [Trying it +> locally](#trying-it-locally) to run the gem from source or via a local path +> dependency. + +Once published, install the gem and add it to the application's Gemfile by +executing: ```bash bundle add password_forge @@ -97,11 +103,26 @@ A non-positive or non-integer `length` raises `ArgumentError`. | `numeric_case` | `0`–`9` | | `special_case` | `` !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ `` | +## Trying it locally + +You can run the gem without installing it from RubyGems: + +- **From this repository:** `ruby examples/smoke_test.rb` prints a few sample + passwords straight from the source tree. +- **From another project:** add `gem "password_forge", path: + "/path/to/password-forge-ruby-gem"` to that project's `Gemfile`, run `bundle + install`, then `require "password_forge"`. See + [`examples/local-consumer/`](examples/local-consumer) for a working example. + +Full instructions, including the interactive console and building a local +`.gem`, are in [docs/local-testing.md](docs/local-testing.md). + ## Roadmap `PasswordForge` is developed in incremental, tagged releases: - **0.0.1** — Core generator, character sets, validation, tests, docs. +- **0.0.2** — Local-testing examples (`examples/`) and documentation (`docs/`). - **0.1.0** — First public release on RubyGems.org via Trusted Publishing. - **0.2.0** — Kiro skills for gem authors (feature TDD, version bump, release). - **0.3.0** — Project `.kiro/` folder with steering and conventions. @@ -113,9 +134,12 @@ A non-positive or non-integer `length` raises `ArgumentError`. After checking out the repo, run `bin/setup` to install dependencies. Then run `bundle exec rake` to run the tests and the linter. You can also run -`bin/console` for an interactive prompt to experiment. +`bin/console` for an interactive prompt to experiment, or +`ruby examples/smoke_test.rb` for a quick check straight from source. -To install this gem onto your local machine, run `bundle exec rake install`. +To install this gem onto your local machine, run `bundle exec rake install`. For +the full local workflow — including running it from a separate project and +uninstalling — see [docs/local-testing.md](docs/local-testing.md). ## Contributing diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..c8e5d65 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,14 @@ +# PasswordForge documentation + +Extended documentation for the `password_forge` gem. This folder is not shipped +in the published gem; it lives in the repository for contributors and curious +readers. + +## Contents + +- [Local testing](local-testing.md) — how to try the gem locally, both from + this repository and from a separate project that depends on it via a local + path. + +More guides will be added here as the project grows (see the roadmap in the +[main README](../README.md)). diff --git a/docs/local-testing.md b/docs/local-testing.md new file mode 100644 index 0000000..ce9639f --- /dev/null +++ b/docs/local-testing.md @@ -0,0 +1,132 @@ +# Trying PasswordForge locally + +This guide shows how to run the gem without publishing it, both from inside this +repository and from a separate project that depends on it via a local path. + +All commands assume you start from the repository root +(`password-forge-ruby-gem/`) unless stated otherwise. + +## 1. From this folder + +### Run the smoke test + +The quickest check. It loads the gem straight from `lib/` (no install needed) +and prints a few sample passwords: + +```bash +ruby examples/smoke_test.rb +``` + +You should see a default password, a numeric PIN, a letters-only password, and +the `NoCharsetSelectedError` being raised on purpose. + +### Use the interactive console + +`bin/console` boots an IRB session with the gem already required: + +```bash +bin/console +``` + +```ruby +PasswordForge.generate +PasswordForge::Generator.new(length: 32).generate +``` + +### Install it onto your machine + +To install the current checkout as a normal gem on your system: + +```bash +bundle exec rake install +``` + +After that, `require "password_forge"` works from any Ruby project on the same +machine. + +## 2. From a different project (local path dependency) + +This is how you consume the gem from another application while developing it, +without pushing anything to RubyGems. + +In the other project's `Gemfile`, point Bundler at your checkout: + +```ruby +# Gemfile of your other project +source "https://rubygems.org" + +gem "password_forge", path: "/absolute/path/to/password-forge-ruby-gem" +``` + +Then, from that project: + +```bash +bundle install +``` + +```ruby +require "password_forge" + +PasswordForge.generate +``` + +Bundler resolves the gem from the given path, so a plain `require` works as if +the gem had been installed from RubyGems. Any edit you make in the gem source is +picked up on the next run — no reinstall needed. + +### Ready-made example + +A working example of this setup lives in +[`examples/local-consumer/`](../examples/local-consumer). It is a minimal +project whose `Gemfile` references the gem with a relative path (`../..`): + +```bash +cd examples/local-consumer +bundle install +bundle exec ruby run.rb +``` + +## 3. Alternative: build and install a .gem + +If you prefer to test the packaged artifact rather than a path dependency: + +```bash +gem build password_forge.gemspec +gem install ./password_forge-0.0.2.gem +``` + +This installs the exact files that would ship to RubyGems (the `examples/` and +`docs/` folders are intentionally excluded from the package). + +## 4. Uninstalling / switching to the published gem + +If you installed the gem locally (via `rake install` or `gem install`) and want +to remove it, use `gem uninstall` rather than deleting files by hand: + +```bash +gem uninstall password_forge +``` + +To see what is installed, or to target a specific version: + +```bash +gem list password_forge +gem uninstall password_forge -v 0.0.2 +``` + +Once the gem is published, switching to the RubyGems release is a plain +uninstall followed by a normal install: + +```bash +gem uninstall password_forge +gem install password_forge +``` + +> **Note:** `password_forge` is not on RubyGems yet (the first public release is +> planned for 0.1.0 — see the roadmap in the [main README](../README.md)). Until +> then, `gem install password_forge` will not find it, so the local install and +> path-dependency approaches above are the way to try it. +> +> While developing, the path dependency in section 2 is usually the most +> convenient: there is nothing to install or uninstall, and source edits are +> picked up on the next run. diff --git a/examples/local-consumer/Gemfile b/examples/local-consumer/Gemfile new file mode 100644 index 0000000..74bd98f --- /dev/null +++ b/examples/local-consumer/Gemfile @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# Minimal "external" project that consumes password_forge from local source. +# +# The path is relative to this Gemfile: two levels up is the gem repo root, +# where password_forge.gemspec lives. In a real project you would point this +# at wherever you checked out the gem, e.g.: +# +# gem "password_forge", path: "/absolute/path/to/password-forge-ruby-gem" +# +source "https://rubygems.org" + +gem "password_forge", path: "../.." diff --git a/examples/local-consumer/run.rb b/examples/local-consumer/run.rb new file mode 100644 index 0000000..4f4fdcb --- /dev/null +++ b/examples/local-consumer/run.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +# Demo entry point for the local-consumer example project. +# +# This mimics an external application that depends on password_forge. Bundler +# resolves the gem from the local path declared in the Gemfile, so a plain +# `require "password_forge"` loads it as if it had been installed normally. +# +# Run it from this directory: +# +# bundle install +# bundle exec ruby run.rb +# +require "password_forge" + +puts "Using password_forge v#{PasswordForge::VERSION} from a local path dependency." +puts "Generated password: #{PasswordForge.generate}" diff --git a/examples/smoke_test.rb b/examples/smoke_test.rb new file mode 100755 index 0000000..79e69f8 --- /dev/null +++ b/examples/smoke_test.rb @@ -0,0 +1,38 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Smoke test for PasswordForge. +# +# Loads the gem straight from the local source tree (no install required) and +# prints a few sample passwords so you can eyeball that everything works. +# +# Run it from the repository root: +# +# ruby examples/smoke_test.rb +# +require_relative "../lib/password_forge" + +puts "PasswordForge v#{PasswordForge::VERSION} — smoke test" +puts "-" * 48 + +puts "Default (all sets, length 16):" +puts " #{PasswordForge.generate}" + +puts "Numeric PIN (length 4):" +puts " #{PasswordForge::Generator.new( + upper_case: false, lower_case: false, numeric_case: true, special_case: false, length: 4 +).generate}" + +puts "Letters only (length 24):" +puts " #{PasswordForge::Generator.new( + numeric_case: false, special_case: false, length: 24 +).generate}" + +puts "No character set selected (expected error):" +begin + PasswordForge::Generator.new( + upper_case: false, lower_case: false, numeric_case: false, special_case: false + ) +rescue PasswordForge::NoCharsetSelectedError => e + puts " raised #{e.class}: #{e.message}" +end diff --git a/lib/password_forge/version.rb b/lib/password_forge/version.rb index 76c4aa4..6093ea9 100644 --- a/lib/password_forge/version.rb +++ b/lib/password_forge/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module PasswordForge - VERSION = "0.0.1" + VERSION = "0.0.2" end diff --git a/password_forge.gemspec b/password_forge.gemspec index bd4d6e5..7d641a1 100644 --- a/password_forge.gemspec +++ b/password_forge.gemspec @@ -28,7 +28,7 @@ Gem::Specification.new do |spec| spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls| ls.readlines("\x0", chomp: true).reject do |f| (f == gemspec) || - f.start_with?(*%w[bin/ Gemfile .gitignore .rspec spec/ .github/ .rubocop.yml]) + f.start_with?(*%w[bin/ examples/ docs/ Gemfile .gitignore .rspec spec/ .github/ .rubocop.yml]) end end spec.bindir = "exe"