petit_poucet 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 31a0dd9277ecdaa4109bc2baaf3d8a7bc7c7e63ddc25d04986b384476d2b2129
4
+ data.tar.gz: 11148aa356349a3d75ff866b972ba628f6d5d389df322aaee6316b06ba87792d
5
+ SHA512:
6
+ metadata.gz: 41d0d30be7ca055999c3901aa0704323e15329d87d709eed97e12a840f5b9ee439a53cffc279f1a5a9184354ed588d9d8b279bfa34e47e78a60e0527e6a895a7
7
+ data.tar.gz: de911c4ee152f4ea74804b4b9c5f930d306189fde6cd1c021294a73a1381d708a09aaa88fcbaceb4f507f0397bb0b6cba765ba5074a959fd2b52f9acbdeb2412
data/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.0.0] - 2025-12-03
11
+
12
+ ### Added
13
+
14
+ - Declarative `breadcrumb` DSL for controllers (class and instance level)
15
+ - Action filtering with `only:` and `except:` options
16
+ - Dynamic breadcrumbs with lambdas and symbols
17
+ - `breadcrumb_trail` view helper with `CrumbPresenter`
18
+ - `current?` method to identify the last breadcrumb
19
+ - `render_breadcrumbs` simple view helper with customizable separator and class
20
+ - `clear_breadcrumbs` to reset inherited breadcrumbs
21
+ - Rails 7.0+ and Ruby 3.0+ support
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Sébastien Loyer
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # Petit Poucet
2
+
3
+ [![CI](https://github.com/Sbastien/petit_poucet/actions/workflows/ci.yml/badge.svg)](https://github.com/Sbastien/petit_poucet/actions/workflows/ci.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/petit_poucet.svg)](https://rubygems.org/gems/petit_poucet)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ > *Le petit Pouçet les laissoit crier, sçachant bien par où il reviendroit à la maison ; car en marchant il avoit laissé tomber le long du chemin les petits cailloux blancs qu'il avoit dans ses poches.*
8
+ >
9
+ > — Charles Perrault, *Le Petit Poucet* (1697)
10
+
11
+ A minimal breadcrumbs gem for Rails. Like the clever boy from the fairy tale who left pebbles to find his way home, this gem helps users navigate back through your application.
12
+
13
+ ## Installation
14
+
15
+ ```ruby
16
+ gem "petit_poucet"
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Controller
22
+
23
+ ```ruby
24
+ class ApplicationController < ActionController::Base
25
+ breadcrumb -> { t("home") }, :root_path
26
+ end
27
+
28
+ class ArticlesController < ApplicationController
29
+ breadcrumb "Articles", :articles_path
30
+ breadcrumb -> { @article.title }, only: [:show, :edit, :update]
31
+
32
+ def show
33
+ @article = Article.find(params[:id])
34
+ end
35
+ end
36
+ ```
37
+
38
+ ### DSL Options
39
+
40
+ ```ruby
41
+ # Static
42
+ breadcrumb "Dashboard", :dashboard_path
43
+
44
+ # Dynamic name
45
+ breadcrumb -> { t("breadcrumbs.home") }, :root_path
46
+
47
+ # Dynamic path
48
+ breadcrumb "Profile", -> { user_path(current_user) }
49
+
50
+ # Action filtering
51
+ breadcrumb "Edit", :edit_article_path, only: [:edit, :update]
52
+ breadcrumb "Details", :articles_path, except: :index
53
+
54
+ # Action filtering without path
55
+ breadcrumb "Current", only: :show
56
+
57
+ # No link (current page)
58
+ breadcrumb -> { @article.title }
59
+ ```
60
+
61
+ ### Runtime Breadcrumbs
62
+
63
+ ```ruby
64
+ def show
65
+ @article = Article.find(params[:id])
66
+ breadcrumb @article.title, article_path(@article)
67
+ breadcrumb "Details" # No link
68
+ end
69
+ ```
70
+
71
+ ### View Rendering
72
+
73
+ #### Simple (built-in helper)
74
+
75
+ ```erb
76
+ <%= render_breadcrumbs %>
77
+ <%# => <nav class="breadcrumb"><a href="/">Home</a> / Articles / My Article</nav> %>
78
+
79
+ <%= render_breadcrumbs(class: "my-breadcrumb", separator: " > ") %>
80
+ ```
81
+
82
+ #### Custom (full control)
83
+
84
+ ```erb
85
+ <nav aria-label="Breadcrumb">
86
+ <ol>
87
+ <% breadcrumb_trail do |crumb| %>
88
+ <li>
89
+ <% if crumb.current? %>
90
+ <%= crumb.name %>
91
+ <% else %>
92
+ <%= link_to crumb.name, crumb.path %>
93
+ <% end %>
94
+ </li>
95
+ <% end %>
96
+ </ol>
97
+ </nav>
98
+ ```
99
+
100
+ ### CrumbPresenter
101
+
102
+ | Method | Description |
103
+ |------------|--------------------------|
104
+ | `name` | Display text |
105
+ | `path` | URL (can be nil) |
106
+ | `current?` | `true` if last breadcrumb |
107
+ | `to_s` | Returns `name` |
108
+
109
+ ### Clearing Inherited Breadcrumbs
110
+
111
+ ```ruby
112
+ class AdminController < ApplicationController
113
+ clear_breadcrumbs
114
+ breadcrumb "Admin", :admin_root_path
115
+ end
116
+ ```
117
+
118
+ ## Requirements
119
+
120
+ - Ruby >= 3.0
121
+ - Rails >= 7.0
122
+
123
+ ## License
124
+
125
+ MIT
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PetitPoucet
4
+ module ControllerMethods
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :_breadcrumb_definitions, default: []
9
+ helper_method :breadcrumbs if respond_to?(:helper_method)
10
+ end
11
+
12
+ class_methods do
13
+ # Declare a breadcrumb at the class level
14
+ #
15
+ # @param name [String, Symbol, Proc] The breadcrumb label
16
+ # @param path [String, Symbol, Proc, nil] The breadcrumb URL
17
+ # @param options [Hash] Options including :only and :except
18
+ #
19
+ def breadcrumb(name, path = nil, **options)
20
+ self._breadcrumb_definitions = _breadcrumb_definitions + [Crumb.new(name, path, **options)]
21
+ end
22
+
23
+ # Clear all inherited breadcrumbs
24
+ def clear_breadcrumbs
25
+ self._breadcrumb_definitions = []
26
+ end
27
+ end
28
+
29
+ # Add a breadcrumb dynamically within an action
30
+ #
31
+ # @param name [String, Proc] The breadcrumb label
32
+ # @param path [String, nil] The breadcrumb URL
33
+ #
34
+ def breadcrumb(name, path = nil)
35
+ @runtime_breadcrumbs ||= []
36
+ @runtime_breadcrumbs << Crumb.new(name, path)
37
+ end
38
+
39
+ # Clear runtime breadcrumbs
40
+ def clear_breadcrumbs
41
+ @runtime_breadcrumbs = []
42
+ end
43
+
44
+ # Get all resolved breadcrumbs for the current action
45
+ #
46
+ # @return [Array<Hash>] Array of { name:, path: } hashes
47
+ #
48
+ def breadcrumbs
49
+ @breadcrumbs ||= resolve_breadcrumbs
50
+ end
51
+
52
+ private
53
+
54
+ def resolve_breadcrumbs
55
+ crumbs = _breadcrumb_definitions.select { |c| c.applies_to_action?(action_name) }
56
+ crumbs += @runtime_breadcrumbs || []
57
+ crumbs.map { |crumb| { name: crumb.resolve_name(self), path: crumb.resolve_path(self) } }
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PetitPoucet
4
+ class Crumb
5
+ attr_reader :name, :path
6
+
7
+ def initialize(name, path = nil, **options)
8
+ @name = name
9
+
10
+ if path.is_a?(Hash)
11
+ options = path
12
+ @path = nil
13
+ else
14
+ @path = path
15
+ end
16
+
17
+ @only_actions = options[:only] && Array(options[:only]).map(&:to_s)
18
+ @except_actions = options[:except] && Array(options[:except]).map(&:to_s)
19
+ end
20
+
21
+ def resolve_name(context)
22
+ case name
23
+ when Proc then context.instance_exec(&name)
24
+ when Symbol then context.public_send(name)
25
+ else name.to_s
26
+ end
27
+ end
28
+
29
+ def resolve_path(context)
30
+ return nil if path.nil?
31
+
32
+ case path
33
+ when Proc then context.instance_exec(&path)
34
+ when Symbol then context.public_send(path)
35
+ else path.to_s
36
+ end
37
+ end
38
+
39
+ def applies_to_action?(action_name)
40
+ return false if @only_actions && !@only_actions.include?(action_name)
41
+ return false if @except_actions&.include?(action_name)
42
+
43
+ true
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PetitPoucet
4
+ # Presenter for a single breadcrumb in views
5
+ #
6
+ # @example
7
+ # crumb.name # => "Home"
8
+ # crumb.path # => "/"
9
+ # crumb.current? # => false
10
+ #
11
+ class CrumbPresenter
12
+ attr_reader :name, :path
13
+
14
+ def initialize(name:, path:, current:)
15
+ @name = name
16
+ @path = path
17
+ @current = current
18
+ end
19
+
20
+ def current?
21
+ @current
22
+ end
23
+
24
+ def to_s
25
+ name
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PetitPoucet
4
+ class Railtie < Rails::Railtie
5
+ initializer 'petit_poucet.configure' do
6
+ ActiveSupport.on_load(:action_controller) do
7
+ include PetitPoucet::ControllerMethods
8
+ end
9
+
10
+ ActiveSupport.on_load(:action_view) do
11
+ include PetitPoucet::ViewHelpers
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PetitPoucet
4
+ VERSION = '1.0.0'
5
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PetitPoucet
4
+ module ViewHelpers
5
+ # Iterate over breadcrumbs with full control over rendering
6
+ #
7
+ # @yield [crumb] Block called for each breadcrumb
8
+ # @yieldparam crumb [CrumbPresenter] Presenter with name, path, current?
9
+ # @return [Array<CrumbPresenter>] All breadcrumbs if no block given
10
+ #
11
+ # @example Without block
12
+ # breadcrumb_trail.each { |crumb| ... }
13
+ #
14
+ # @example With block
15
+ # breadcrumb_trail do |crumb|
16
+ # concat link_to(crumb.name, crumb.path) unless crumb.current?
17
+ # end
18
+ #
19
+ def breadcrumb_trail(&block)
20
+ crumbs = breadcrumbs.each_with_index.map do |crumb, index|
21
+ CrumbPresenter.new(
22
+ name: crumb[:name],
23
+ path: crumb[:path],
24
+ current: index == breadcrumbs.size - 1
25
+ )
26
+ end
27
+
28
+ return crumbs unless block_given?
29
+
30
+ crumbs.each(&block)
31
+ end
32
+
33
+ # Simple default renderer for breadcrumbs
34
+ #
35
+ # @param options [Hash] Rendering options
36
+ # @option options [String] :class CSS class for the container ('breadcrumb')
37
+ # @option options [String] :separator Separator between crumbs (' / ')
38
+ #
39
+ # @return [ActiveSupport::SafeBuffer, nil] HTML or nil if no breadcrumbs
40
+ #
41
+ # @example Default rendering
42
+ # render_breadcrumbs
43
+ # # => <nav class="breadcrumb">Home / Articles / My Article</nav>
44
+ #
45
+ # @example Custom options
46
+ # render_breadcrumbs(class: 'custom-breadcrumb', separator: ' > ')
47
+ #
48
+ def render_breadcrumbs(options = {})
49
+ return if breadcrumbs.empty?
50
+
51
+ css_class = options[:class] || 'breadcrumb'
52
+ separator = options[:separator] || ' / '
53
+
54
+ items = breadcrumb_trail.map do |crumb|
55
+ if crumb.path.present? && !crumb.current?
56
+ link_to(crumb.name, crumb.path)
57
+ else
58
+ crumb.name
59
+ end
60
+ end
61
+
62
+ content_tag(:nav, safe_join(items, separator.html_safe), class: css_class)
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'petit_poucet/version'
4
+ require_relative 'petit_poucet/crumb'
5
+ require_relative 'petit_poucet/crumb_presenter'
6
+ require_relative 'petit_poucet/controller_methods'
7
+ require_relative 'petit_poucet/view_helpers'
8
+ require_relative 'petit_poucet/railtie' if defined?(Rails::Railtie)
9
+
10
+ module PetitPoucet
11
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: petit_poucet
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Sébastien Loyer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-12-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: actionpack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '7.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '7.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '7.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '7.0'
41
+ description: Minimal breadcrumbs gem with declarative DSL and action filtering
42
+ email:
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - CHANGELOG.md
48
+ - LICENSE.txt
49
+ - README.md
50
+ - lib/petit_poucet.rb
51
+ - lib/petit_poucet/controller_methods.rb
52
+ - lib/petit_poucet/crumb.rb
53
+ - lib/petit_poucet/crumb_presenter.rb
54
+ - lib/petit_poucet/railtie.rb
55
+ - lib/petit_poucet/version.rb
56
+ - lib/petit_poucet/view_helpers.rb
57
+ homepage: https://github.com/Sbastien/petit_poucet
58
+ licenses:
59
+ - MIT
60
+ metadata:
61
+ homepage_uri: https://github.com/Sbastien/petit_poucet
62
+ source_code_uri: https://github.com/Sbastien/petit_poucet
63
+ changelog_uri: https://github.com/Sbastien/petit_poucet/blob/main/CHANGELOG.md
64
+ rubygems_mfa_required: 'true'
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '3.0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubygems_version: 3.5.22
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: Modern breadcrumbs DSL for Rails
84
+ test_files: []