kpumuk/meta-tags
Search Engine Optimization (SEO) for Ruby on Rails applications.
Healthy across all four use cases
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 3d ago
- ✓4 active contributors
- ✓MIT licensed
Show 4 more →Show less
- ✓CI configured
- ✓Tests present
- ⚠Small team — 4 contributors active in recent commits
- ⚠Single-maintainer risk — top contributor 80% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/kpumuk/meta-tags)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/kpumuk/meta-tags on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: kpumuk/meta-tags
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/kpumuk/meta-tags shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- Last commit 3d ago
- 4 active contributors
- MIT licensed
- CI configured
- Tests present
- ⚠ Small team — 4 contributors active in recent commits
- ⚠ Single-maintainer risk — top contributor 80% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live kpumuk/meta-tags
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/kpumuk/meta-tags.
What it runs against: a local clone of kpumuk/meta-tags — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in kpumuk/meta-tags | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 33 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of kpumuk/meta-tags. If you don't
# have one yet, run these first:
#
# git clone https://github.com/kpumuk/meta-tags.git
# cd meta-tags
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of kpumuk/meta-tags and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "kpumuk/meta-tags(\\.git)?\\b" \\
&& ok "origin remote is kpumuk/meta-tags" \\
|| miss "origin remote is not kpumuk/meta-tags (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "lib/meta_tags.rb" \\
&& ok "lib/meta_tags.rb" \\
|| miss "missing critical file: lib/meta_tags.rb"
test -f "lib/meta_tags/view_helper.rb" \\
&& ok "lib/meta_tags/view_helper.rb" \\
|| miss "missing critical file: lib/meta_tags/view_helper.rb"
test -f "lib/meta_tags/renderer.rb" \\
&& ok "lib/meta_tags/renderer.rb" \\
|| miss "missing critical file: lib/meta_tags/renderer.rb"
test -f "lib/meta_tags/controller_helper.rb" \\
&& ok "lib/meta_tags/controller_helper.rb" \\
|| miss "missing critical file: lib/meta_tags/controller_helper.rb"
test -f "lib/meta_tags/configuration.rb" \\
&& ok "lib/meta_tags/configuration.rb" \\
|| miss "missing critical file: lib/meta_tags/configuration.rb"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 33 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~3d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/kpumuk/meta-tags"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
MetaTags is a Ruby on Rails gem that renders HTML head metadata for SEO and social sharing—titles, descriptions, canonical links, robots directives, Open Graph tags, X card tags, and hreflang links. It provides Rails helpers and configuration to manage the metadata layer of search engine optimization without generating structured JSON-LD or sitemaps. Single-gem structure: lib/meta-tags/ contains the core logic (configuration.rb, content_tag.rb, etc.), lib/generators/ holds the install generator, and gemfiles/ contains appraisal-managed test suites for each Rails version. The gem is configured via config/initializers/meta_tags.rb after running the install generator.
👥Who it's for
Rails developers building public-facing web applications who need to control SEO metadata and social media preview cards without manually writing meta tag HTML. They use it to set page titles, descriptions, canonicals, and Open Graph properties dynamically from their controllers or views.
🌱Maturity & risk
Production-ready and actively maintained. The repo shows comprehensive CI/CD with GitHub Actions testing across Rails 6.1 through 8.1 and Ruby 3.0+, strict code quality checks (RuboCop, Steep type checking, YARD linting), and a detailed CHANGELOG. Recent workflows and gemfile locks for Rails 8.1 indicate active development.
Low risk for a gem this focused. Single maintainer (kpumuk) and relatively small codebase (132KB Ruby), but the test matrix across 8 Rails versions and multiple Ruby versions plus dependabot automation mitigates maintenance burden. No obvious breaking changes in recent history; the gem targets stable Rails LTS versions.
Active areas of work
Active maintenance of Rails 8.0/8.1 compatibility (visible in gemfiles/rails_8.0.gemfile and rails_8.1.gemfile with recent locks). Automated dependabot PRs for dependency updates, standard.rb linting, and Steep type checking are running in CI. No major feature work evident, but steady bug fixes and compatibility updates.
🚀Get running
git clone https://github.com/kpumuk/meta-tags.git && cd meta-tags && bundle install && bundle exec rspec
Daily commands: bundle exec rspec runs the test suite. bin/console loads an interactive Rails console for the gem. CI runs via GitHub Actions: standard.rb linting (workflows/standard.yml), Steep type checks (workflows/steep.yml), and RSpec tests across matrix in workflows/tests.yml.
🗺️Map of the codebase
lib/meta_tags.rb— Main entry point that requires all core modules and initializes the gem; essential for understanding how components are loaded.lib/meta_tags/view_helper.rb— Core view helper that exposes SEO metadata rendering to Rails templates; the primary public API for end users.lib/meta_tags/renderer.rb— Converts meta tag data structures into safe HTML output; critical for understanding tag generation and escaping logic.lib/meta_tags/controller_helper.rb— Provides controller-level methods to set metadata before view rendering; bridges controller and view layers.lib/meta_tags/configuration.rb— Manages gem-wide configuration and defaults; foundation for customization across all features.lib/meta_tags/meta_tags_collection.rb— Stores and manages the collection of meta tags throughout a request lifecycle; central data structure.lib/meta_tags/railtie.rb— Rails engine integration that registers helpers and initializers; makes the gem available to Rails apps.
🧩Components & responsibilities
- ViewHelper (Rails ActionView::Helpers) — Exposes set_meta_tags and display_meta_tags to templates; parses input and delegates to collection and renderer
- Failure mode: If ViewHelper fails, developers cannot set or render metadata from views; entire gem becomes unusable
- ControllerHelper (Rails ActionController) — Provides controller-level equivalent of set_meta_tags; allows metadata configuration before view render
- Failure mode: Controller-level metadata setting fails; developers must set all metadata in views
- MetaTagsCollection (Ruby Thread.current) — Thread-local storage for a request's metadata; acts as the single source of truth for all tags set during request
- Failure mode: If collection loses data, subsequent calls to display_meta_tags render incomplete HTML; data loss across request
- Renderer (ERB, Rails ActionView) — Converts tag collection into safe, escaped HTML; applies normalization and escaping rules
- Failure mode: If renderer fails, HTML is malformed or unescaped; potential XSS vulnerability or broken markup
- TextNormalizer (String manipulation) — Truncates and sanitizes metadata text (titles, descriptions) to meet platform limits and rules
- Failure mode: Text is not truncated; metadata becomes too long and is truncated by search engines inconsistently
🔀Data flow
Rails view template→ViewHelper.set_meta_tags— Developer calls set_meta_tags with Hash of metadata (title,
🛠️How to make changes
Add a new meta tag type (e.g., custom proprietary tag)
- Update lib/meta_tags/meta_tags_collection.rb to recognize the new tag type in the allowed tags list or add a new handler method (
lib/meta_tags/meta_tags_collection.rb) - Add a public method to lib/meta_tags/view_helper.rb (e.g., set_custom_tag) that normalizes and stores the tag (
lib/meta_tags/view_helper.rb) - Update lib/meta_tags/renderer.rb to generate the appropriate HTML output in the render_tags method (
lib/meta_tags/renderer.rb) - Add test spec in spec/view_helper/custom_spec.rb to validate output and behavior (
spec/view_helper/custom_spec.rb)
Add a new normalization rule for metadata text
- Add a new method to lib/meta_tags/text_normalizer.rb (e.g., normalize_custom_text) with your truncation, sanitization, or transformation logic (
lib/meta_tags/text_normalizer.rb) - Call the new normalizer method in lib/meta_tags/view_helper.rb where metadata is initially set (
lib/meta_tags/view_helper.rb) - Add test file spec/text_normalizer/normalize_custom_spec.rb with test cases (
spec/text_normalizer)
Add a new configuration option
- Add the new option as an attr_accessor to lib/meta_tags/configuration.rb and set a sensible default (
lib/meta_tags/configuration.rb) - Update lib/generators/meta_tags/templates/config/initializers/meta_tags.rb to show the new option in the generated initializer (
lib/generators/meta_tags/templates/config/initializers/meta_tags.rb) - Reference the config option in lib/meta_tags/view_helper.rb or lib/meta_tags/renderer.rb where the feature is implemented (
lib/meta_tags/view_helper.rb) - Add test to spec/configuration_spec.rb to verify the option is loaded and defaults work correctly (
spec/configuration_spec.rb)
🔧Why these technologies
- Ruby on Rails Railtie — Provides seamless integration with Rails applications without requiring boilerplate; automatically registers view helpers and initializers
- View helpers (ERB context) — Allows direct access to Rails rendering context and template variables; natural fit for HTML generation in Rails
- Type hints (RBS signatures) — Provides static type checking and IDE autocomplete for Ruby developers using Steep; optional but improves DX
⚖️Trade-offs already made
-
Thread-local storage for meta tag collection per request
- Why: Avoids needing to thread the collection through every method call; simpler API for view layer
- Consequence: Relies on Rails request context; not suitable for non-request use cases (e.g., background jobs, standalone scripts)
-
Normalization (truncation, sanitization) at set time rather than render time
- Why: Predictable output; metadata is normalized once and reused if displayed multiple times
- Consequence: No ability to dynamically adjust rendering based on context; all tags follow the same normalization rules
-
HTML escaping at render time in the Renderer class
- Why: Centralizes security logic; easier to audit and maintain single escape point
- Consequence: All tag data is escaped uniformly; requires careful handling if any tag legitimately needs HTML content
🚫Non-goals (don't propose these)
- Does not handle authentication or access control for metadata visibility
- Does not provide SEO auditing or scoring beyond metadata generation
- Does not integrate with external analytics platforms or tracking services
- Does not generate structured data (JSON-LD) or microformats; only HTML meta tags
- Does not validate SEO best practices or warn about missing recommended tags
🪤Traps & gotchas
Meta tag truncation behavior differs based on config.truncate_array_items_at_boundaries (truncates individual items vs. preserving whole items in arrays). The gem uses 'property' attribute for certain tags (Open Graph, custom-configured ones) and 'name' for others—mismatched tags will render with wrong attributes. Rails must be 6.1+ and Ruby 3.0+; older versions are no longer supported. Some tags like keywords are legacy and not SEO-critical, but still supported for compatibility.
🏗️Architecture
💡Concepts to learn
- Meta tag truncation strategies — Search engines and social platforms have character limits for titles/descriptions; this gem's truncate_array_items_at_boundaries config determines whether to preserve semantic boundaries (whole items) or truncate mid-item
- Open Graph protocol — Meta-tags must render property= instead of name= for OG tags; the gem maintains a configurable whitelist of such tags for social platform sharing previews
- Canonical URLs — The gem helps prevent duplicate content SEO penalties by managing canonical link elements; critical for pagination and parameter variations
- Robots meta directives — Meta-tags renders robot control directives (noindex, nofollow, etc.) that instruct search engines how to treat pages; the gem abstracts this configuration
- Rails Engine architecture — Meta-tags is packaged as a Rails Engine or gem; understanding how engines hook into app initialization, generators, and helper modules is essential for extending it
- hreflang link elements — The gem supports hreflang for multi-language/region canonicalization; Google uses these to serve correct language versions and avoid duplicate indexing across locales
- Appraisals gem for gemfile matrices — Meta-tags uses Appraisals to generate isolated Gemfiles for each Rails version (6.1–8.1) and test them in CI; this pattern ensures backward compatibility without monolithic dependency management
🔗Related repos
rails/rails— Core Rails framework that meta-tags extends; understanding ActionView helpers and Engine architecture is essentialkpumuk/acts-as-taggable-on— Same maintainer's tagging gem; demonstrates similar Rails plugin patterns and gemfile-based testing approachjoshbuchea/HEAD— Comprehensive reference for all HTML head elements and metadata standards that meta-tags implements (not a code repo, but educational)ankane/strong_migrations— Comparable Rails gem ecosystem project with multi-version testing, Appraisals, and strict code quality (RuboCop, tests)thoughtbot/bitters— Rails engine/gem pattern used here; illustrates how to structure a reusable Rails plugin with generators and initializers
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive type signature stubs for view_helper.rb and renderer.rb
The repo has Steep type checking enabled (.github/workflows/steep.yml) and RBS signature files in sig/, but sig/lib/meta_tags/view_helper.rbs and sig/lib/meta_tags/renderer.rbs are missing. These are core public APIs that users interact with. Adding complete type signatures would improve IDE autocomplete, catch bugs, and serve as executable documentation.
- [ ] Create sig/lib/meta_tags/view_helper.rbs with type signatures for all public methods (display_meta_tags, set_meta_tags, etc.)
- [ ] Create sig/lib/meta_tags/renderer.rbs with type signatures for rendering pipeline methods
- [ ] Run steep check to validate signatures against lib/meta_tags/view_helper.rb and lib/meta_tags/renderer.rb
- [ ] Verify CI passes in .github/workflows/steep.yml
Add integration tests for Rails 6.1 and 7.0 Gemfiles in CI matrix
The repo supports Rails 6.1+ (evident from gemfiles/ directory with rails_6.1.gemfile through rails_8.1.gemfile), but .github/workflows/tests.yml likely only tests against one or two Rails versions. Adding a matrix job to test against each Gemfile ensures compatibility across all supported Rails versions and catches regressions early.
- [ ] Review .github/workflows/tests.yml to identify current Rails versions being tested
- [ ] Add a matrix strategy that tests against each gemfiles/rails_*.gemfile (6.1, 7.0, 7.1, 7.2, 8.0, 8.1)
- [ ] Use appraisal or bundler to install dependencies from each gemfile before running rspec
- [ ] Verify all tests pass across the full matrix locally before pushing
Add missing documentation for text_normalizer.rb in README or YARD docs
lib/meta_tags/text_normalizer.rb exists as a public utility but isn't documented in README.md. Users may not know about text normalization features (likely whitespace/truncation handling for SEO). Adding documentation and completing YARD annotations would help users leverage this feature and improve yard-lint.yml compliance.
- [ ] Review lib/meta_tags/text_normalizer.rb to understand its public API and use cases
- [ ] Add comprehensive YARD comments (@param, @return, @example) to all public methods
- [ ] Add a 'Text Normalization' section to README.md with examples of how/when text_normalizer is used
- [ ] Run yard-lint to verify documentation completeness (.github/workflows/yard-lint.yml)
🌿Good first issues
- Add type stubs or RBS definitions for the public gem API (lib/meta_tags.rb, tag_helpers, configuration) to improve Steep coverage and help IDEs—many gems lack proper types
- Expand YARD documentation examples in lib/meta_tags/tag_helpers.rb by adding real-world controller/view usage snippets for Open Graph and X card tags
- Add integration test for the install generator (lib/generators/meta_tags/install_generator.rb) verifying that rails generate meta_tags:install creates config/initializers/meta_tags.rb with correct defaults
⭐Top contributors
Click to expand
Top contributors
- @kpumuk — 80 commits
- @dependabot[bot] — 18 commits
- @dlackty — 1 commits
- @kataokatsuki — 1 commits
📝Recent commits
Click to expand
Recent commits
7d4fe8e— Merge pull request #372 from kpumuk/dependabot/github_actions/ruby/setup-ruby-1.306.0 (kpumuk)adeef70— Bump ruby/setup-ruby from 1.302.0 to 1.306.0 (dependabot[bot])e104590— Merge pull request #370 from kpumuk/dependabot/github_actions/ruby/setup-ruby-1.302.0 (kpumuk)a5b7d62— Bump ruby/setup-ruby from 1.301.0 to 1.302.0 (dependabot[bot])93e0d03— Merge pull request #367 from kpumuk/dependabot/github_actions/actions/upload-artifact-7.0.1 (kpumuk)237aab0— Bump actions/upload-artifact from 7.0.0 to 7.0.1 (dependabot[bot])00853b0— Merge pull request #368 from kpumuk/dependabot/github_actions/actions/create-github-app-token-3.1.1 (kpumuk)d454377— Bump actions/create-github-app-token from 3.0.0 to 3.1.1 (dependabot[bot])1119831— Merge pull request #369 from kpumuk/dependabot/github_actions/ruby/setup-ruby-1.301.0 (kpumuk)bbbc0a8— Bump ruby/setup-ruby from 1.300.0 to 1.301.0 (dependabot[bot])
🔒Security observations
The meta-tags gem demonstrates reasonable security practices with a defined security policy, use of standard Ruby tooling (Rubocop, Steep type checking), and automated CI/CD pipelines. However, the primary security concern is potential XSS vulnerabilities in meta tag rendering, which is critical for a library handling user-supplied content. The codebase lacks visible evidence of security testing (no SAST configuration in .codeclimate.yml beyond style checking) and the certificate file in the repo represents a minor hygiene issue. Recommendations include comprehensive XSS testing, security-focused code review of rendering logic, and completing security documentation. The OpenSSF Scorecard integration is a positive sign of security awareness.
- Medium · Potential XSS Vulnerability in Meta Tag Rendering —
lib/meta_tags/renderer.rb, lib/meta_tags/content_tag.rb, lib/meta_tags/view_helper.rb. The meta-tags gem is designed to render HTML head metadata. Without explicit review of the renderer.rb and content_tag.rb files, there is a risk that user-supplied content (titles, descriptions, og:tags) could be rendered without proper HTML escaping, leading to Cross-Site Scripting (XSS) vulnerabilities. This is especially critical in SEO libraries that handle user-controlled content. Fix: Ensure all user-supplied content is properly HTML-escaped before rendering. Use Rails' html_escape or sanitize methods. Verify that meta tag values are escaped contextually (attribute vs text content). Add comprehensive test coverage for XSS injection attempts with special characters and HTML entities. - Low · Signed Certificate Present in Repository —
certs/kpumuk.pem. A signing certificate file (certs/kpumuk.pem) is committed to the repository. While this appears to be a public certificate for gem signing, committing any certificate files to version control can be a security hygiene issue if the private key is ever accidentally included or if the certificate needs to be revoked. Fix: Consider documenting the certificate's purpose and that it is public-only. Use a .gitignore rule for certificate files by default. If any private keys were committed historically, rotate them immediately. Review git history to ensure no private keys were ever committed. - Low · Incomplete Security Policy Documentation —
SECURITY.md. The SECURITY.md file is incomplete (cut off mid-sentence at the 'Disclosure Policy' section). This could leave users uncertain about the complete security disclosure process and timelines. Fix: Complete the SECURITY.md file with full disclosure timeline expectations (e.g., '90 days for patching'), confirmation of fix availability, and credit/acknowledgment policies. - Low · Multiple Rails Version Dependencies —
gemfiles/ (rails_6.1.gemfile through rails_8.1.gemfile). The gem supports Rails versions 6.1 through 8.1 and edge. While this provides flexibility, it increases the complexity of maintaining security patches across different major versions. Older versions may become unmaintained upstream. Fix: Establish a clear support matrix and EOL policy for which Rails versions receive security updates. Document minimum supported versions. Consider dropping support for EOL Rails versions in new releases.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.