From 4efd92cdeaebe44dd018a261724bb3f900560fe2 Mon Sep 17 00:00:00 2001 From: Stelios Frantzeskakis Date: Tue, 19 May 2026 00:12:55 +0300 Subject: [PATCH 1/2] [GH-7352] Use GitHub Pages for documentation --- .github/workflows/docs.yml | 37 ++++++++++++++++++++ .yardopts | 9 +++++ Gemfile | 1 + lib/rubyzen/cache/parse_cache.rb | 1 + lib/rubyzen/collections/base_collection.rb | 1 + lib/rubyzen/declarations/file_declaration.rb | 1 + lib/rubyzen/matchers/matcher_helpers.rb | 7 ++++ lib/rubyzen/matchers/zen_empty_matcher.rb | 30 ++++++++++------ lib/rubyzen/matchers/zen_false_matcher.rb | 34 +++++++++++------- lib/rubyzen/matchers/zen_true_matcher.rb | 25 +++++++++---- lib/rubyzen/parsers/a_s_t_parser.rb | 1 + lib/rubyzen/providers/blocks_provider.rb | 1 + lib/rubyzen/version.rb | 1 + rubyzen-lint.gemspec | 3 +- 14 files changed, 121 insertions(+), 31 deletions(-) create mode 100644 .github/workflows/docs.yml create mode 100644 .yardopts diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..20ab7e3 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,37 @@ +name: Docs + +on: + push: + branches: [main] + +permissions: + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: true + - run: bundle exec yard doc + - uses: actions/upload-pages-artifact@v3 + with: + path: doc + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/.yardopts b/.yardopts new file mode 100644 index 0000000..066b558 --- /dev/null +++ b/.yardopts @@ -0,0 +1,9 @@ +--output-dir doc +--markup markdown +--no-private +--protected +lib/**/*.rb +- +README.md +CONTRIBUTING.md +LICENSE diff --git a/Gemfile b/Gemfile index 97e50fa..7bd051d 100644 --- a/Gemfile +++ b/Gemfile @@ -6,4 +6,5 @@ group :development do gem 'pry', '~> 0.14.1' gem 'ostruct', '~> 0.6.2' gem 'yard', '~> 0.9' + gem 'webrick', '~> 1.8' end diff --git a/lib/rubyzen/cache/parse_cache.rb b/lib/rubyzen/cache/parse_cache.rb index 865f6e3..0b0ae18 100644 --- a/lib/rubyzen/cache/parse_cache.rb +++ b/lib/rubyzen/cache/parse_cache.rb @@ -1,6 +1,7 @@ require 'digest' module Rubyzen + # Caching utilities for parsed AST results. module Cache # In-memory cache for parsed AST results, keyed by file path and SHA256 checksum. # Automatically invalidates entries when file contents change. diff --git a/lib/rubyzen/collections/base_collection.rb b/lib/rubyzen/collections/base_collection.rb index 2cf3d30..e48ba85 100644 --- a/lib/rubyzen/collections/base_collection.rb +++ b/lib/rubyzen/collections/base_collection.rb @@ -1,4 +1,5 @@ module Rubyzen + # Typed collections that wrap arrays of declarations with filtering and aggregation. module Collections # Base collection class for all Rubyzen collections. # Extends Array and replaces +select+/+reject+ with a single +filter+ method diff --git a/lib/rubyzen/declarations/file_declaration.rb b/lib/rubyzen/declarations/file_declaration.rb index 9718dfb..3c03883 100644 --- a/lib/rubyzen/declarations/file_declaration.rb +++ b/lib/rubyzen/declarations/file_declaration.rb @@ -1,4 +1,5 @@ module Rubyzen + # Domain objects wrapping AST nodes with high-level accessors. module Declarations # Represents a parsed Ruby source file. This is the root of the declaration # hierarchy — all other declarations are accessed through a FileDeclaration. diff --git a/lib/rubyzen/matchers/matcher_helpers.rb b/lib/rubyzen/matchers/matcher_helpers.rb index 0c17b9c..391699d 100644 --- a/lib/rubyzen/matchers/matcher_helpers.rb +++ b/lib/rubyzen/matchers/matcher_helpers.rb @@ -1,4 +1,5 @@ module Rubyzen + # Custom RSpec matchers for asserting on Rubyzen collections. module Matchers # Shared helper methods used by Rubyzen's custom RSpec matchers. # @@ -152,6 +153,12 @@ def formatted_matcher_groups sections.join("\n") end + # @!method message_for_failure(base_message) + # Formats the failure message by combining the base message with + # custom messages and classified item details (violations, stale entries). + # + # @param base_message [String] the default failure message + # @return [String] formatted failure message def self.included(base) base.define_method(:message_for_failure) do |base_message| return @failure_message if @failure_message diff --git a/lib/rubyzen/matchers/zen_empty_matcher.rb b/lib/rubyzen/matchers/zen_empty_matcher.rb index bd8cdc2..f28f0cb 100644 --- a/lib/rubyzen/matchers/zen_empty_matcher.rb +++ b/lib/rubyzen/matchers/zen_empty_matcher.rb @@ -1,13 +1,23 @@ -# Custom RSpec matcher that asserts a Rubyzen collection is empty. -# -# Used in architectural lint rules to verify that no items match -# a forbidden pattern (e.g., no controllers call +.where+ directly). -# -# @example Ensure no controllers use .where -# expect(controllers.all_methods.call_sites.with_name('where')).to zen_empty -# -# @example With a custom failure message -# expect(violations).to zen_empty("Controllers should not call .where directly") +# @!parse +# module Rubyzen +# module Matchers +# # Asserts that a Rubyzen collection is empty. +# # +# # Used in architectural lint rules to verify that no items match +# # a forbidden pattern (e.g., no controllers call +.where+ directly). +# # +# # @param custom_message [String, nil] optional failure message +# # @param allowlist [Array, nil] items to permanently ignore +# # @param baseline [Array, nil] known violations for gradual adoption +# # +# # @example Ensure no controllers use .where +# # expect(controllers.all_methods.call_sites.with_name('where')).to zen_empty +# # +# # @example With baseline for gradual adoption +# # expect(violations).to zen_empty(baseline: ['LegacyController']) +# def zen_empty(custom_message = nil, allowlist: nil, baseline: nil); end +# end +# end RSpec::Matchers.define :zen_empty do |custom_message=nil, allowlist: nil, baseline: nil| include Rubyzen::Matchers::MatcherHelpers diff --git a/lib/rubyzen/matchers/zen_false_matcher.rb b/lib/rubyzen/matchers/zen_false_matcher.rb index c65ac0e..27e8677 100644 --- a/lib/rubyzen/matchers/zen_false_matcher.rb +++ b/lib/rubyzen/matchers/zen_false_matcher.rb @@ -1,16 +1,24 @@ -# Custom RSpec matcher that asserts a block returns false for every item in a collection. -# -# Supports +allowlist:+ and +baseline:+ for gradual adoption, matching items -# where the block returns true against exception lists. -# -# @example Ensure no methods have more than 5 parameters -# expect(methods).to zen_false { |m| m.parameters.size > 5 } -# -# @example With a custom failure message -# expect(controllers.all_methods.call_sites).to zen_false("Controllers must not call .where directly") { |cs| cs.name == 'where' } -# -# @example With a baseline for gradual adoption -# expect(classes).to zen_false(baseline: ['LegacyModel']) { |k| k.lines_of_code > 200 } +# @!parse +# module Rubyzen +# module Matchers +# # Asserts that a block returns false for every item in a collection. +# # +# # Supports +allowlist:+ and +baseline:+ for gradual adoption, matching items +# # where the block returns true against exception lists. +# # +# # @param custom_message [String, nil] optional failure message +# # @param allowlist [Array, nil] items to permanently ignore +# # @param baseline [Array, nil] known violations for gradual adoption +# # @yield [item] block that should return false for each item +# # +# # @example Ensure no methods have more than 5 parameters +# # expect(methods).to zen_false { |m| m.parameters.size > 5 } +# # +# # @example With a baseline for gradual adoption +# # expect(classes).to zen_false(baseline: ['LegacyModel']) { |k| k.lines_of_code > 200 } +# def zen_false(custom_message = nil, allowlist: nil, baseline: nil, &block); end +# end +# end RSpec::Matchers.define :zen_false do |custom_message=nil, allowlist: nil, baseline: nil| include Rubyzen::Matchers::MatcherHelpers diff --git a/lib/rubyzen/matchers/zen_true_matcher.rb b/lib/rubyzen/matchers/zen_true_matcher.rb index 4eb23d2..e6b819a 100644 --- a/lib/rubyzen/matchers/zen_true_matcher.rb +++ b/lib/rubyzen/matchers/zen_true_matcher.rb @@ -1,10 +1,21 @@ -# Custom RSpec matcher that asserts a block returns true for every item in a collection. -# -# @example Ensure all methods have parameters -# expect(methods).to zen_true { |m| m.parameters? } -# -# @example With a custom failure message -# expect(services).to zen_true("All services must inherit from BaseService") { |s| s.superclass_name == 'BaseService' } +# @!parse +# module Rubyzen +# module Matchers +# # Asserts that a block returns true for every item in a collection. +# # +# # @param custom_message [String, nil] optional failure message +# # @param allowlist [Array, nil] items to permanently ignore +# # @param baseline [Array, nil] known violations for gradual adoption +# # @yield [item] block that should return true for each item +# # +# # @example Ensure all methods have parameters +# # expect(methods).to zen_true { |m| m.parameters? } +# # +# # @example With a custom failure message +# # expect(services).to zen_true("All services must inherit from BaseService") { |s| s.superclass_name == 'BaseService' } +# def zen_true(custom_message = nil, allowlist: nil, baseline: nil, &block); end +# end +# end RSpec::Matchers.define :zen_true do |custom_message=nil, allowlist: nil, baseline: nil| include Rubyzen::Matchers::MatcherHelpers diff --git a/lib/rubyzen/parsers/a_s_t_parser.rb b/lib/rubyzen/parsers/a_s_t_parser.rb index 1b31463..ea0fe40 100644 --- a/lib/rubyzen/parsers/a_s_t_parser.rb +++ b/lib/rubyzen/parsers/a_s_t_parser.rb @@ -1,6 +1,7 @@ require 'rubocop-ast' module Rubyzen + # Ruby source file parsing utilities. module Parsers # Singleton parser that converts Ruby source files into Rubyzen declarations # using RuboCop's AST processing. Results are cached via {Cache::ParseCache}. diff --git a/lib/rubyzen/providers/blocks_provider.rb b/lib/rubyzen/providers/blocks_provider.rb index 4a77981..36e4b36 100644 --- a/lib/rubyzen/providers/blocks_provider.rb +++ b/lib/rubyzen/providers/blocks_provider.rb @@ -1,4 +1,5 @@ module Rubyzen + # Mixins that add capabilities (call sites, blocks, attributes, etc.) to declarations. module Providers # Provides access to block expressions (do..end / {..}) within a declaration. module BlocksProvider diff --git a/lib/rubyzen/version.rb b/lib/rubyzen/version.rb index 3b5097b..52b8900 100644 --- a/lib/rubyzen/version.rb +++ b/lib/rubyzen/version.rb @@ -1,3 +1,4 @@ module Rubyzen + # @return [String] the current gem version VERSION = '0.1.0' end diff --git a/rubyzen-lint.gemspec b/rubyzen-lint.gemspec index 3f95f78..6fbf77c 100644 --- a/rubyzen-lint.gemspec +++ b/rubyzen-lint.gemspec @@ -23,6 +23,7 @@ Gem::Specification.new do |spec| spec.metadata = { 'source_code_uri' => 'https://github.com/perrystreetsoftware/Rubyzen', - 'bug_tracker_uri' => 'https://github.com/perrystreetsoftware/Rubyzen/issues' + 'bug_tracker_uri' => 'https://github.com/perrystreetsoftware/Rubyzen/issues', + 'documentation_uri' => 'https://perrystreetsoftware.github.io/Rubyzen' } end From 6dffe799aa554ec03027f700df30feab02676b93 Mon Sep 17 00:00:00 2001 From: Stelios Frantzeskakis Date: Tue, 19 May 2026 00:16:29 +0300 Subject: [PATCH 2/2] Update README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index ced2703..6a7f2ee 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Rubyzen +[![Gem Version](https://badge.fury.io/rb/rubyzen-lint.svg)](https://badge.fury.io/rb/rubyzen-lint) +[![CI](https://github.com/perrystreetsoftware/Rubyzen/actions/workflows/tests.yml/badge.svg)](https://github.com/perrystreetsoftware/Rubyzen/actions/workflows/tests.yml) +[![Docs](https://img.shields.io/badge/docs-yard-blue)](https://perrystreetsoftware.github.io/Rubyzen) + Rubyzen is an architectural linter for Ruby that lets you write architectural lint rules as unit tests, inspired by [Konsist](https://github.com/LemonAppDev/konsist) (for Kotlin) and [Harmonize](https://github.com/perrystreetsoftware/Harmonize) (for Swift). ## Architectural linters in the era of AI-generated code