├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ └── ruby-tests.yml ├── .gitignore ├── .rubocop.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE.md ├── README.md ├── Rakefile ├── breadcrumbs.gemspec ├── examples └── myapp │ ├── .gitignore │ ├── Gemfile │ ├── README.rdoc │ ├── Rakefile │ ├── app │ ├── assets │ │ ├── images │ │ │ └── .keep │ │ └── stylesheets │ │ │ └── application.css │ ├── controllers │ │ ├── application_controller.rb │ │ ├── concerns │ │ │ └── .keep │ │ └── site_controller.rb │ ├── helpers │ │ └── application_helper.rb │ ├── mailers │ │ └── .keep │ ├── models │ │ ├── .keep │ │ └── concerns │ │ │ └── .keep │ └── views │ │ ├── layouts │ │ └── application.html.erb │ │ └── site │ │ ├── contact.html.erb │ │ └── home.html.erb │ ├── bin │ ├── bundle │ ├── rails │ ├── rake │ └── setup │ ├── config.ru │ ├── config │ ├── application.rb │ ├── boot.rb │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── initializers │ │ ├── assets.rb │ │ ├── backtrace_silencers.rb │ │ ├── cookies_serializer.rb │ │ ├── filter_parameter_logging.rb │ │ ├── inflections.rb │ │ ├── mime_types.rb │ │ ├── session_store.rb │ │ └── wrap_parameters.rb │ ├── locales │ │ └── en.yml │ ├── routes.rb │ └── secrets.yml │ ├── db │ └── seeds.rb │ ├── lib │ ├── assets │ │ └── .keep │ └── tasks │ │ └── .keep │ ├── log │ └── .keep │ ├── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ ├── favicon.ico │ └── robots.txt │ └── vendor │ └── assets │ └── stylesheets │ └── .keep ├── lib ├── breadcrumbs.rb └── breadcrumbs │ ├── action_controller_ext.rb │ ├── render.rb │ ├── render │ ├── base.rb │ ├── inline.rb │ ├── list.rb │ └── ordered_list.rb │ └── version.rb └── test ├── breadcrumbs_test.rb ├── support └── pt-BR.yml └── test_helper.rb /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # You can read more about CODEOWNERS at 2 | # https://help.github.com/github/creating-cloning-and-archiving-repositories/about-code-owners 3 | 4 | * @fnando 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | --- 3 | github: [fnando] 4 | custom: ["https://paypal.me/nandovieira/🍕"] 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🐛 Bug Report" 3 | about: Report a reproducible bug or regression. 4 | title: "Bug: " 5 | labels: "Status: Unconfirmed" 6 | --- 7 | 8 | 14 | 15 | ## Description 16 | 17 | [Add bug description here] 18 | 19 | ## How to reproduce 20 | 21 | [Add steps on how to reproduce this issue] 22 | 23 | ## What do you expect 24 | 25 | [Describe what do you expect to happen] 26 | 27 | ## What happened instead 28 | 29 | [Describe the actual results] 30 | 31 | ## Software: 32 | 33 | - Gem version: [Add gem version here] 34 | - Ruby version: [Add version here] 35 | 36 | ## Full backtrace 37 | 38 | ```text 39 | [Paste full backtrace here] 40 | ``` 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | contact_links: 3 | - name: "🤨 Q&A" 4 | url: https://github.com/fnando/breadcrumbs/discussions/new?category=q-a 5 | about: Have a question? Ask it away here! 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "💡 Feature request" 3 | about: Have an idea that may be useful? Make a suggestion! 4 | title: 'Feature Request: ' 5 | labels: 'Feature request' 6 | 7 | --- 8 | 9 | ## Description 10 | 11 | _A clear and concise description of what the problem is._ 12 | 13 | ## Describe the solution 14 | 15 | _A clear and concise description of what you want to happen._ 16 | 17 | ## Alternatives you considered 18 | 19 | _A clear and concise description of any alternative solutions or features you've considered._ 20 | 21 | ## Additional context 22 | 23 | _Add any other context, screenshots, links, etc about the feature request here._ 24 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 7 | 8 |
9 | PR Checklist 10 | 11 | ### PR Structure 12 | 13 | - [ ] This PR has reasonably narrow scope (if not, break it down into smaller 14 | PRs). 15 | - [ ] This PR avoids mixing refactoring changes with feature changes (split into 16 | two PRs otherwise). 17 | - [ ] This PR's title starts is concise and descriptive. 18 | 19 | ### Thoroughness 20 | 21 | - [ ] This PR adds tests for the most critical parts of the new functionality or 22 | fixes. 23 | - [ ] I've updated any docs, `.md` files, etc… affected by this change. 24 | 25 |
26 | 27 | ### What 28 | 29 | [TODO: Short statement about what is changing.] 30 | 31 | ### Why 32 | 33 | [TODO: Why this change is being made. Include any context required to understand 34 | the why.] 35 | 36 | ### Known limitations 37 | 38 | [TODO or N/A] 39 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Documentation: 3 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 4 | 5 | version: 2 6 | updates: 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | 12 | - package-ecosystem: npm 13 | directory: "/" 14 | schedule: 15 | interval: "daily" 16 | 17 | - package-ecosystem: bundler 18 | directory: "/" 19 | schedule: 20 | interval: "daily" 21 | -------------------------------------------------------------------------------- /.github/workflows/ruby-tests.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: ruby-tests 3 | 4 | on: 5 | pull_request_target: 6 | push: 7 | branches: 8 | - main 9 | workflow_dispatch: 10 | inputs: {} 11 | 12 | jobs: 13 | build: 14 | name: Tests with Ruby ${{ matrix.ruby }} and ${{ matrix.gemfile }} 15 | runs-on: "ubuntu-latest" 16 | if: | 17 | github.actor == 'dependabot[bot]' && github.event_name == 'pull_request_target' || 18 | github.actor != 'dependabot[bot]' 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | ruby: ["2.7", "3.0", "3.1", "3.2"] 23 | gemfile: 24 | - Gemfile 25 | 26 | steps: 27 | - uses: actions/checkout@v3 28 | 29 | - uses: actions/cache@v3 30 | with: 31 | path: vendor/bundle 32 | key: > 33 | ${{ runner.os }}-${{ matrix.ruby }}-gems-${{ 34 | hashFiles(matrix.gemfile) }} 35 | 36 | - name: Set up Ruby 37 | uses: ruby/setup-ruby@v1 38 | with: 39 | ruby-version: ${{ matrix.ruby }} 40 | 41 | - name: Install gem dependencies 42 | env: 43 | BUNDLE_GEMFILE: ${{ matrix.gemfile }} 44 | run: | 45 | gem install bundler 46 | bundle config path vendor/bundle 47 | bundle update --jobs 4 --retry 3 48 | 49 | - name: Run Tests 50 | env: 51 | BUNDLE_GEMFILE: ${{ matrix.gemfile }} 52 | run: | 53 | bundle exec rake 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pkg 2 | doc 3 | *.lock 4 | coverage 5 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | inherit_gem: 3 | rubocop-fnando: .rubocop.yml 4 | 5 | AllCops: 6 | TargetRubyVersion: 2.7 7 | NewCops: enable 8 | Exclude: 9 | - examples/**/* 10 | - vendor/**/* 11 | - gemfiles/**/* 12 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at me@fnando.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [https://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: https://contributor-covenant.org 74 | [version]: https://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to breadcrumbs 2 | 3 | 👍🎉 First off, thanks for taking the time to contribute! 🎉👍 4 | 5 | The following is a set of guidelines for contributing to this project. These are 6 | mostly guidelines, not rules. Use your best judgment, and feel free to propose 7 | changes to this document in a pull request. 8 | 9 | ## Code of Conduct 10 | 11 | Everyone interacting in this project's codebases, issue trackers, chat rooms and 12 | mailing lists is expected to follow the 13 | [code of conduct](https://github.com/fnando/breadcrumbs/blob/main/CODE_OF_CONDUCT.md). 14 | 15 | ## Reporting bugs 16 | 17 | This section guides you through submitting a bug report. Following these 18 | guidelines helps maintainers and the community understand your report, reproduce 19 | the behavior, and find related reports. 20 | 21 | - Before creating bug reports, please check the open issues; somebody may 22 | already have submitted something similar, and you may not need to create a new 23 | one. 24 | - When you are creating a bug report, please include as many details as 25 | possible, with an example reproducing the issue. 26 | 27 | ## Contributing with code 28 | 29 | Before making any radicals changes, please make sure you discuss your intention 30 | by [opening an issue on Github](https://github.com/fnando/breadcrumbs/issues). 31 | 32 | When you're ready to make your pull request, follow checklist below to make sure 33 | your contribution is according to how this project works. 34 | 35 | 1. [Fork](https://help.github.com/forking/) breadcrumbs 36 | 2. Create a topic branch - `git checkout -b my_branch` 37 | 3. Make your changes using [descriptive commit messages](#commit-messages) 38 | 4. Update CHANGELOG.md describing your changes by adding an entry to the 39 | "Unreleased" section. If this section is not available, create one right 40 | before the last version. 41 | 5. Push to your branch - `git push origin my_branch` 42 | 6. [Create a pull request](https://help.github.com/articles/creating-a-pull-request) 43 | 7. That's it! 44 | 45 | ## Styleguides 46 | 47 | ### Commit messages 48 | 49 | - Use the present tense ("Add feature" not "Added feature") 50 | - Use the imperative mood ("Move cursor to..." not "Moves cursor to...") 51 | - Limit the first line to 72 characters or less 52 | - Reference issues and pull requests liberally after the first line 53 | 54 | ### Changelog 55 | 56 | - Add a message describing your changes to the "Unreleased" section. The 57 | changelog message should follow the same style as the commit message. 58 | - Prefix your message with one of the following: 59 | - `[Added]` for new features. 60 | - `[Changed]` for changes in existing functionality. 61 | - `[Deprecated]` for soon-to-be removed features. 62 | - `[Removed]` for now removed features. 63 | - `[Fixed]` for any bug fixes. 64 | - `[Security]` in case of vulnerabilities. 65 | 66 | ### Ruby code 67 | 68 | - This project uses [Rubocop](https://rubocop.org) to enforce code style. Before 69 | submitting your changes, make sure your tests are passing and code conforms to 70 | the expected style by running `rake`. 71 | - Do not change the library version. This will be done by the maintainer 72 | whenever a new version is about to be released. 73 | 74 | ### JavaScript code 75 | 76 | - This project uses [ESLint](https://eslint.org) to enforce code style. Before 77 | submitting your changes, make sure your tests are passing and code conforms to 78 | the expected style by running `yarn test:ci`. 79 | - Do not change the library version. This will be done by the maintainer 80 | whenever a new version is about to be released. 81 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Nando Vieira 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Breadcrumbs 2 | 3 | [![ruby-tests](https://github.com/fnando/breadcrumbs/actions/workflows/ruby-tests.yml/badge.svg)](https://github.com/fnando/breadcrumbs/actions/workflows/ruby-tests.yml) 4 | [![Gem](https://img.shields.io/gem/v/breadcrumbs.svg)](https://rubygems.org/gems/breadcrumbs) 5 | [![Gem](https://img.shields.io/gem/dt/breadcrumbs.svg)](https://rubygems.org/gems/breadcrumbs) 6 | 7 | Breadcrumbs is a simple plugin that adds a `breadcrumbs` object to controllers 8 | and views. 9 | 10 | ## Instalation 11 | 12 | Just run `gem install breadcrumbs`. Or add `gem "breadcrumbs"` to your Gemfile. 13 | 14 | ## Usage 15 | 16 | On your controller (optional): 17 | 18 | ```ruby 19 | class ApplicationController < ActionController::Base 20 | before_action :add_initial_breadcrumbs 21 | 22 | private 23 | def add_initial_breadcrumbs 24 | breadcrumbs.add "Home", root_path 25 | end 26 | end 27 | 28 | class ThingsController < ApplicationController 29 | def index 30 | breadcrumbs.add "Things", things_path 31 | end 32 | end 33 | ``` 34 | 35 | You don't need to provide an URL; in that case, a span will be generated instead 36 | of a link: 37 | 38 | ```ruby 39 | breadcrumbs.add "Some page" 40 | ``` 41 | 42 | You can set additional HTML attributes if you need to: 43 | 44 | ```ruby 45 | breadcrumbs.add "Home", root_path, id: "home", title: "Go to the home page" 46 | ``` 47 | 48 | On your view (possibly application.html.erb): 49 | 50 | ```erb 51 | <%= breadcrumbs.render %> 52 | ``` 53 | 54 | You can render as ordered list. 55 | 56 | ```erb 57 | <%= breadcrumbs.render(format: :ordered_list) %> 58 | ``` 59 | 60 | You can render as inline links. 61 | 62 | ```erb 63 | <%= breadcrumbs.render(format: :inline) %> 64 | ``` 65 | 66 | You can set your own separator: 67 | 68 | ```erb 69 |

70 | You are here: <%= breadcrumbs.render(format: :inline, separator: "|") %> 71 |

72 | ``` 73 | 74 | You can also define your own formatter. Just create a class that implements a 75 | `render` instance method and you're good to go. 76 | 77 | ```ruby 78 | class Breadcrumbs::Render::Dl 79 | def render 80 | # return breadcrumbs wrapped in a
tag 81 | end 82 | end 83 | ``` 84 | 85 | To use your new format, just provide the `:format` option. 86 | 87 | ```ruby 88 | breadcrumbs.render(format: :dl) 89 | ``` 90 | 91 | ### I18n 92 | 93 | Breadcrumbs is integrated with I18n. You can set translations like: 94 | 95 | ```yaml 96 | en: 97 | breadcrumbs: 98 | home: "Home" 99 | ``` 100 | 101 | And then you just call 102 | 103 | ```ruby 104 | breadcrumbs.add :home 105 | ``` 106 | 107 | In fact, you can provide any scope you want. 108 | 109 | ```ruby 110 | breadcrumbs.add :"titles.home" 111 | ``` 112 | 113 | If you don't want to translate a label, just pass the option `:i18n` as `false`. 114 | 115 | ```ruby 116 | breadcrumbs.add :home, nil, i18n: false 117 | ``` 118 | 119 | ## Maintainer 120 | 121 | - [Nando Vieira](https://github.com/fnando) 122 | 123 | ## Contributors 124 | 125 | - https://github.com/fnando/breadcrumbs/contributors 126 | 127 | ## Contributing 128 | 129 | For more details about how to contribute, please read 130 | https://github.com/fnando/breadcrumbs/blob/main/CONTRIBUTING.md. 131 | 132 | ## License 133 | 134 | The gem is available as open source under the terms of the 135 | [MIT License](https://opensource.org/licenses/MIT). A copy of the license can be 136 | found at https://github.com/fnando/breadcrumbs/blob/main/LICENSE.md. 137 | 138 | ## Code of Conduct 139 | 140 | Everyone interacting in the breadcrumbs project's codebases, issue trackers, 141 | chat rooms and mailing lists is expected to follow the 142 | [code of conduct](https://github.com/fnando/breadcrumbs/blob/main/CODE_OF_CONDUCT.md). 143 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "bundler/gem_tasks" 4 | require "rake/testtask" 5 | require "rubocop/rake_task" 6 | 7 | Rake::TestTask.new(:test) do |t| 8 | t.libs << "test" 9 | t.test_files = FileList["test/**/*_test.rb"] 10 | t.warning = false 11 | end 12 | 13 | RuboCop::RakeTask.new 14 | 15 | task default: %i[test rubocop] 16 | -------------------------------------------------------------------------------- /breadcrumbs.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "./lib/breadcrumbs/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "breadcrumbs" 7 | s.version = Breadcrumbs::Version::STRING 8 | s.platform = Gem::Platform::RUBY 9 | s.authors = ["Nando Vieira"] 10 | s.email = ["fnando.vieira@gmail.com"] 11 | s.homepage = "http://rubygems.org/gems/breadcrumbs" 12 | s.summary = "Breadcrumbs is a simple plugin that adds a `breadcrumbs` " \ 13 | "object to controllers and views." 14 | s.description = s.summary 15 | s.required_ruby_version = Gem::Requirement.new(">= 2.7.0") 16 | 17 | github_url = "https://github.com/fnando/breadcrumbs" 18 | github_tree_url = "#{github_url}/tree/v#{s.version}" 19 | 20 | s.metadata["homepage_uri"] = s.homepage 21 | s.metadata["bug_tracker_uri"] = "#{github_url}/issues" 22 | s.metadata["source_code_uri"] = github_tree_url 23 | s.metadata["changelog_uri"] = "#{github_tree_url}/CHANGELOG.md" 24 | s.metadata["documentation_uri"] = "#{github_tree_url}/README.md" 25 | s.metadata["license_uri"] = "#{github_tree_url}/LICENSE.md" 26 | s.metadata["rubygems_mfa_required"] = "true" 27 | 28 | s.files = `git ls-files`.split("\n") 29 | s.executables = `git ls-files -- bin/*` 30 | .split("\n") 31 | .map {|f| File.basename(f) } 32 | s.require_paths = ["lib"] 33 | 34 | s.add_dependency "actionview" 35 | s.add_dependency "activesupport" 36 | s.add_dependency "i18n" 37 | 38 | s.add_development_dependency "actionpack" 39 | s.add_development_dependency "bundler" 40 | s.add_development_dependency "minitest-utils" 41 | s.add_development_dependency "mocha" 42 | s.add_development_dependency "nokogiri" 43 | s.add_development_dependency "pry-meta" 44 | s.add_development_dependency "rake" 45 | s.add_development_dependency "rubocop" 46 | s.add_development_dependency "rubocop-fnando" 47 | s.add_development_dependency "simplecov" 48 | end 49 | -------------------------------------------------------------------------------- /examples/myapp/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all logfiles and tempfiles. 11 | /log/*.log 12 | /tmp 13 | -------------------------------------------------------------------------------- /examples/myapp/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rails', '4.2.0.beta1' 4 | gem 'sass-rails', '~> 5.0.0.beta1' 5 | gem 'uglifier', '>= 1.3.0' 6 | gem 'rails-html-sanitizer', '~> 1.0' 7 | gem 'breadcrumbs', path: '../..' 8 | 9 | group :development, :test do 10 | gem 'spring' 11 | end 12 | 13 | -------------------------------------------------------------------------------- /examples/myapp/README.rdoc: -------------------------------------------------------------------------------- 1 | == README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Things you may want to cover: 7 | 8 | * Ruby version 9 | 10 | * System dependencies 11 | 12 | * Configuration 13 | 14 | * Database creation 15 | 16 | * Database initialization 17 | 18 | * How to run the test suite 19 | 20 | * Services (job queues, cache servers, search engines, etc.) 21 | 22 | * Deployment instructions 23 | 24 | * ... 25 | 26 | 27 | Please feel free to use a different markup language if you do not plan to run 28 | rake doc:app. 29 | -------------------------------------------------------------------------------- /examples/myapp/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /examples/myapp/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnando/breadcrumbs/756da079c4e14dbd2e153377927aa7ef1d056aa3/examples/myapp/app/assets/images/.keep -------------------------------------------------------------------------------- /examples/myapp/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | *= require_tree . 3 | *= require_self 4 | */ 5 | 6 | .breadcrumbs { 7 | font-family: sans-serif; 8 | font-size: 14px; 9 | background: #f5f5f5; 10 | list-style: none; 11 | margin: 0; 12 | padding: 10px; 13 | overflow: hidden; 14 | color: #777; 15 | border-radius: 5px; 16 | } 17 | 18 | .breadcrumbs li { 19 | float: left; 20 | } 21 | 22 | .breadcrumbs li + li:before { 23 | content: '/'; 24 | color: #ccc; 25 | margin: 0 10px; 26 | } 27 | 28 | .breadcrumbs a { 29 | color: #428bca; 30 | text-decoration: none; 31 | } 32 | 33 | .breadcrumbs a:hover { 34 | text-decoration: underline; 35 | } 36 | 37 | .breadcrumbs .first { 38 | position: relative; 39 | padding-left: 18px; 40 | } 41 | 42 | .breadcrumbs .first:before { 43 | content: '\2302'; 44 | font-size: 20px; 45 | position: absolute; 46 | left: 0; 47 | top: -2px; 48 | color: #ccc; 49 | } 50 | -------------------------------------------------------------------------------- /examples/myapp/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | # Prevent CSRF attacks by raising an exception. 3 | # For APIs, you may want to use :null_session instead. 4 | protect_from_forgery with: :exception 5 | 6 | before_action :set_root_breadcrumb 7 | 8 | private 9 | 10 | def set_root_breadcrumb 11 | breadcrumbs.add :home, root_path 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /examples/myapp/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnando/breadcrumbs/756da079c4e14dbd2e153377927aa7ef1d056aa3/examples/myapp/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /examples/myapp/app/controllers/site_controller.rb: -------------------------------------------------------------------------------- 1 | class SiteController < ApplicationController 2 | def home 3 | end 4 | 5 | def contact 6 | breadcrumbs.add :pages 7 | breadcrumbs.add :contact, contact_path 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /examples/myapp/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /examples/myapp/app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnando/breadcrumbs/756da079c4e14dbd2e153377927aa7ef1d056aa3/examples/myapp/app/mailers/.keep -------------------------------------------------------------------------------- /examples/myapp/app/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnando/breadcrumbs/756da079c4e14dbd2e153377927aa7ef1d056aa3/examples/myapp/app/models/.keep -------------------------------------------------------------------------------- /examples/myapp/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnando/breadcrumbs/756da079c4e14dbd2e153377927aa7ef1d056aa3/examples/myapp/app/models/concerns/.keep -------------------------------------------------------------------------------- /examples/myapp/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Myapp 5 | <%= stylesheet_link_tag 'application', media: 'all' %> 6 | <%= csrf_meta_tags %> 7 | 8 | 9 | 10 | <%= breadcrumbs.render(format: 'list') %> 11 | 12 | <%= yield %> 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/myapp/app/views/site/contact.html.erb: -------------------------------------------------------------------------------- 1 |

Contact

2 | -------------------------------------------------------------------------------- /examples/myapp/app/views/site/home.html.erb: -------------------------------------------------------------------------------- 1 |

Home page

2 | -------------------------------------------------------------------------------- /examples/myapp/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /examples/myapp/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../../config/application', __FILE__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /examples/myapp/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /examples/myapp/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | # path to your application root. 5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 6 | 7 | Dir.chdir APP_ROOT do 8 | # This script is a starting point to setup your application. 9 | # Add necessary setup steps to this file: 10 | 11 | puts "== Installing dependencies ==" 12 | system "gem install bundler --conservative" 13 | system "bundle check || bundle install" 14 | 15 | # puts "\n== Copying sample files ==" 16 | # unless File.exist?("config/database.yml") 17 | # system "cp config/database.yml.sample config/database.yml" 18 | # end 19 | 20 | puts "\n== Preparing database ==" 21 | system "bin/rake db:setup" 22 | 23 | puts "\n== Removing old logs and tempfiles ==" 24 | system "rm -f log/*" 25 | system "rm -rf tmp/cache" 26 | 27 | puts "\n== Restarting application server ==" 28 | system "touch tmp/restart.txt" 29 | end 30 | -------------------------------------------------------------------------------- /examples/myapp/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Rails.application 5 | -------------------------------------------------------------------------------- /examples/myapp/config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | # Pick the frameworks you want: 4 | require "active_model/railtie" 5 | # require "active_record/railtie" 6 | require "action_controller/railtie" 7 | require "action_mailer/railtie" 8 | require "action_view/railtie" 9 | require "sprockets/railtie" 10 | # require "rails/test_unit/railtie" 11 | 12 | # Require the gems listed in Gemfile, including any gems 13 | # you've limited to :test, :development, or :production. 14 | Bundler.require(*Rails.groups) 15 | 16 | module Myapp 17 | class Application < Rails::Application 18 | # Settings in config/environments/* take precedence over those specified here. 19 | # Application configuration should go into files in config/initializers 20 | # -- all .rb files in that directory are automatically loaded. 21 | 22 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 23 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 24 | # config.time_zone = 'Central Time (US & Canada)' 25 | 26 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 27 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 28 | # config.i18n.default_locale = :de 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /examples/myapp/config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /examples/myapp/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /examples/myapp/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports and disable caching. 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send. 17 | config.action_mailer.raise_delivery_errors = false 18 | 19 | # Print deprecation notices to the Rails logger. 20 | config.active_support.deprecation = :log 21 | 22 | # Debug mode disables concatenation and preprocessing of assets. 23 | # This option may cause significant delays in view rendering with a large 24 | # number of complex assets. 25 | config.assets.debug = true 26 | 27 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 28 | # yet still be able to expire them through the digest params. 29 | config.assets.digest = true 30 | 31 | # Adds additional error checking when serving assets at runtime. 32 | # Checks for improperly declared sprockets dependencies. 33 | # Raises helpful error messages. 34 | config.assets.raise_runtime_errors = true 35 | 36 | # Raises error for missing translations 37 | # config.action_view.raise_on_missing_translations = true 38 | end 39 | -------------------------------------------------------------------------------- /examples/myapp/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 18 | # Add `rack-cache` to your Gemfile before enabling this. 19 | # For large-scale production use, consider using a caching reverse proxy like NGINX, varnish or squid. 20 | # config.action_dispatch.rack_cache = true 21 | 22 | # Disable Rails's static asset server (Apache or NGINX will already do this). 23 | config.serve_static_assets = false 24 | 25 | # Compress JavaScripts and CSS. 26 | config.assets.js_compressor = :uglifier 27 | # config.assets.css_compressor = :sass 28 | 29 | # Do not fallback to assets pipeline if a precompiled asset is missed. 30 | config.assets.compile = false 31 | 32 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 33 | # yet still be able to expire them through the digest params. 34 | config.assets.digest = true 35 | 36 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 37 | 38 | # Specifies the header that your server uses for sending files. 39 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache 40 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 41 | 42 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 43 | # config.force_ssl = true 44 | 45 | # Set to :info to decrease the log volume. 46 | config.log_level = :debug 47 | 48 | # Prepend all log lines with the following tags. 49 | # config.log_tags = [ :subdomain, :uuid ] 50 | 51 | # Use a different logger for distributed setups. 52 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 53 | 54 | # Use a different cache store in production. 55 | # config.cache_store = :mem_cache_store 56 | 57 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 58 | # config.action_controller.asset_host = "http://assets.example.com" 59 | 60 | # Ignore bad email addresses and do not raise email delivery errors. 61 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 62 | # config.action_mailer.raise_delivery_errors = false 63 | 64 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 65 | # the I18n.default_locale when a translation cannot be found). 66 | config.i18n.fallbacks = true 67 | 68 | # Send deprecation notices to registered listeners. 69 | config.active_support.deprecation = :notify 70 | 71 | # Use default logging formatter so that PID and timestamp are not suppressed. 72 | config.log_formatter = ::Logger::Formatter.new 73 | end 74 | -------------------------------------------------------------------------------- /examples/myapp/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure static asset server for tests with Cache-Control for performance. 16 | config.serve_static_assets = true 17 | config.static_cache_control = 'public, max-age=3600' 18 | 19 | # Show full error reports and disable caching. 20 | config.consider_all_requests_local = true 21 | config.action_controller.perform_caching = false 22 | 23 | # Raise exceptions instead of rendering exception templates. 24 | config.action_dispatch.show_exceptions = false 25 | 26 | # Disable request forgery protection in test environment. 27 | config.action_controller.allow_forgery_protection = false 28 | 29 | # Tell Action Mailer not to deliver emails to the real world. 30 | # The :test delivery method accumulates sent emails in the 31 | # ActionMailer::Base.deliveries array. 32 | config.action_mailer.delivery_method = :test 33 | 34 | # Print deprecation notices to the stderr. 35 | config.active_support.deprecation = :stderr 36 | 37 | # Raises error for missing translations 38 | # config.action_view.raise_on_missing_translations = true 39 | end 40 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 11 | # Rails.application.config.assets.precompile += %w( search.js ) 12 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.action_dispatch.cookies_serializer = :json 4 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_myapp_session' 4 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters) 9 | end 10 | -------------------------------------------------------------------------------- /examples/myapp/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | 25 | breadcrumbs: 26 | home: 'My app' 27 | pages: 'Pages' 28 | contact: 'Contact' 29 | -------------------------------------------------------------------------------- /examples/myapp/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root to: 'site#home' 3 | get :contact, to: 'site#contact' 4 | resources :projects 5 | end 6 | -------------------------------------------------------------------------------- /examples/myapp/config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 7343b6667cab06eb7ee86cc0d29fdeff6f3ea8354fc7eabcbbc8e7e370a35169906267a0adf23fc7dd983eb700867c72c346f9c5f507067dca4e7d07028fa687 15 | 16 | test: 17 | secret_key_base: dc87916f434557d81ddee2465a49a0cdd9fd5227ca5fd0ddb5798d5f33d2f8750688ca5d90d70fd7010450e6547d05aae44291de9a2623121408e666b0d644a6 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /examples/myapp/db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | -------------------------------------------------------------------------------- /examples/myapp/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnando/breadcrumbs/756da079c4e14dbd2e153377927aa7ef1d056aa3/examples/myapp/lib/assets/.keep -------------------------------------------------------------------------------- /examples/myapp/lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnando/breadcrumbs/756da079c4e14dbd2e153377927aa7ef1d056aa3/examples/myapp/lib/tasks/.keep -------------------------------------------------------------------------------- /examples/myapp/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnando/breadcrumbs/756da079c4e14dbd2e153377927aa7ef1d056aa3/examples/myapp/log/.keep -------------------------------------------------------------------------------- /examples/myapp/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /examples/myapp/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /examples/myapp/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /examples/myapp/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnando/breadcrumbs/756da079c4e14dbd2e153377927aa7ef1d056aa3/examples/myapp/public/favicon.ico -------------------------------------------------------------------------------- /examples/myapp/public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /examples/myapp/vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnando/breadcrumbs/756da079c4e14dbd2e153377927aa7ef1d056aa3/examples/myapp/vendor/assets/stylesheets/.keep -------------------------------------------------------------------------------- /lib/breadcrumbs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "i18n" 4 | require "action_view" 5 | require "active_support/inflector" 6 | 7 | require "breadcrumbs/render" 8 | require "breadcrumbs/action_controller_ext" if defined?(ActionController) 9 | 10 | class Breadcrumbs 11 | attr_accessor :items 12 | 13 | def initialize # :nodoc: 14 | self.items = [] 15 | end 16 | 17 | # Add a new breadcrumbs. 18 | # 19 | # breadcrumbs.add 'Home' 20 | # breadcrumbs.add 'Home', '/' 21 | # breadcrumbs.add 'Home', '/', class: 'home' 22 | # 23 | # If you provide a symbol as text, it will try to 24 | # find it as I18n scope. 25 | # 26 | def add(text, url = nil, options = {}) 27 | options = {i18n: true}.merge(options) 28 | text = translate(text) if options.delete(:i18n) 29 | items << [text.to_s, url, options] 30 | end 31 | 32 | alias << add 33 | 34 | # Render breadcrumbs using the specified format. 35 | # Use HTML lists by default, but can be plain links. 36 | # 37 | # breadcrumbs.render 38 | # breadcrumbs.render(format: "inline") 39 | # breadcrumbs.render(format: "inline", separator: "|") 40 | # breadcrumbs.render(format: "list") 41 | # breadcrumbs.render(format: "ordered_list") 42 | # breadcrumbs.render(id: 'breadcrumbs') 43 | # breadcrumbs.render(class: 'breadcrumbs') 44 | # 45 | # You can also define your own formatter. Just create a class that implements 46 | # a +render+ instance 47 | # method and you're good to go. 48 | # 49 | # class Breadcrumbs::Render::Dl 50 | # def render 51 | # # return breadcrumbs wrapped in a
tag 52 | # end 53 | # end 54 | # 55 | # To use your new format, just provide the :format option. 56 | # 57 | # breadcrumbs.render(format: 'dl') 58 | # 59 | def render(options = {}) 60 | options[:format] ||= :list 61 | 62 | klass_name = options.delete(:format).to_s.classify 63 | klass = Breadcrumbs::Render.const_get(klass_name) 64 | klass.new(self, options).render 65 | end 66 | 67 | def translate(scope) # :nodoc: 68 | return scope if scope.match?(/\A[\s.]+\z/) 69 | 70 | text = begin 71 | I18n.t(scope, scope: "breadcrumbs", raise: true) 72 | rescue StandardError 73 | nil 74 | end 75 | 76 | text ||= begin 77 | I18n.t(scope, default: scope.to_s) 78 | rescue StandardError 79 | scope 80 | end 81 | 82 | text 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /lib/breadcrumbs/action_controller_ext.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Breadcrumbs 4 | module ActionController # :nodoc: all 5 | def self.included(base) 6 | base.send :helper_method, :breadcrumbs 7 | end 8 | 9 | def breadcrumbs 10 | @breadcrumbs ||= Breadcrumbs.new 11 | end 12 | end 13 | end 14 | 15 | ActionController::Base.include Breadcrumbs::ActionController 16 | -------------------------------------------------------------------------------- /lib/breadcrumbs/render.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Breadcrumbs 4 | module Render 5 | require "breadcrumbs/render/base" 6 | require "breadcrumbs/render/inline" 7 | require "breadcrumbs/render/list" 8 | require "breadcrumbs/render/ordered_list" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/breadcrumbs/render/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Breadcrumbs 4 | module Render 5 | class Base # :nodoc: all 6 | include ::ActionView::Helpers::TagHelper 7 | 8 | attr_accessor :breadcrumbs, :default_options, :output_buffer 9 | 10 | def initialize(breadcrumbs, default_options = {}) 11 | @breadcrumbs = breadcrumbs 12 | @default_options = default_options 13 | end 14 | 15 | def tag(name, content = "", options = {}, &block) 16 | content_tag(name, content, options, &block) 17 | end 18 | 19 | protected def wrap_item(url, text, options) 20 | if url 21 | tag(:a, text, options.merge(href: url)) 22 | else 23 | tag(:span, text, options) 24 | end 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/breadcrumbs/render/inline.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Breadcrumbs 4 | module Render 5 | class Inline < Base # :nodoc: all 6 | def render 7 | options = { 8 | class: "breadcrumbs", 9 | separator: "»" 10 | }.merge(default_options) 11 | 12 | html = [] 13 | items = breadcrumbs.items 14 | size = items.size 15 | 16 | items.each_with_index do |item, i| 17 | html << render_item(item, i, size) 18 | end 19 | 20 | separator = tag(:span, options[:separator], class: "separator") 21 | 22 | html.join(" #{separator} ").html_safe 23 | end 24 | 25 | def render_item(item, i, size) 26 | text, url, options = *item 27 | 28 | css = [options[:class]].compact 29 | css << "first" if i.zero? 30 | css << "last" if i == size - 1 31 | css << "item-#{i}" 32 | 33 | options[:class] = css.join(" ") 34 | options[:class].gsub!(/^ *(.*?)$/, '\\1') 35 | 36 | wrap_item(url, text, options) 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/breadcrumbs/render/list.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Breadcrumbs 4 | module Render 5 | class List < Base # :nodoc: all 6 | def render 7 | options = {class: "breadcrumbs"}.merge(default_options) 8 | 9 | tag(list_style, options) do 10 | html = [] 11 | items = breadcrumbs.items 12 | size = items.size 13 | 14 | items.each_with_index do |item, i| 15 | html << render_item(item, i, size) 16 | end 17 | 18 | html.join.html_safe 19 | end 20 | end 21 | 22 | def list_style 23 | :ul 24 | end 25 | 26 | def render_item(item, i, size) 27 | css = [] 28 | css << "first" if i.zero? 29 | css << "last" if i == size - 1 30 | css << "item-#{i}" 31 | 32 | text, url, options = *item 33 | text = wrap_item(url, text, options) 34 | tag(:li, text, class: css.join(" ")) 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/breadcrumbs/render/ordered_list.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Breadcrumbs 4 | module Render 5 | class OrderedList < List # :nodoc: all 6 | def list_style 7 | :ol 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/breadcrumbs/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Breadcrumbs 4 | module Version # :nodoc: all 5 | MAJOR = 0 6 | MINOR = 3 7 | PATCH = 0 8 | STRING = "#{MAJOR}.#{MINOR}.#{PATCH}" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/breadcrumbs_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class BreadcrumbsTest < Minitest::Test 6 | def setup 7 | @breadcrumbs = Breadcrumbs.new 8 | @inline = Breadcrumbs::Render::Inline.new(@breadcrumbs) 9 | end 10 | 11 | test "returns safe html" do 12 | assert @breadcrumbs.render(format: "list").html_safe? 13 | end 14 | 15 | test "adds item" do 16 | @breadcrumbs.add "Home" 17 | 18 | assert_equal 1, @breadcrumbs.items.count 19 | 20 | @breadcrumbs << "Home" 21 | 22 | assert_equal 2, @breadcrumbs.items.count 23 | end 24 | 25 | test "renders tag with attributes" do 26 | expected = %[Hi!] 27 | 28 | assert_equal expected, 29 | @inline.tag(:span, "Hi!", class: "greetings", id: "hi") 30 | end 31 | 32 | test "renders tag with block" do 33 | assert_equal "Hi!", @inline.tag(:span) { "Hi!" } 34 | end 35 | 36 | test "renders tag with block and attributes" do 37 | expected = %[Hi!] 38 | 39 | assert_equal expected, 40 | @inline.tag(:span, class: "greetings", id: "hi") { "Hi!" } 41 | end 42 | 43 | test "renders nested tags" do 44 | expected = %[Hi!] 45 | actual = @inline.tag(:span, class: "greetings") do 46 | @inline.tag(:strong, "Hi!", id: "hi") 47 | end 48 | 49 | assert_equal expected, actual 50 | end 51 | 52 | test "renders as list" do 53 | @breadcrumbs.add "Home", "/", class: "home" 54 | html = Nokogiri::HTML(@breadcrumbs.render) 55 | 56 | refute_nil html.at("ul.breadcrumbs") 57 | assert_nil html.at("ul.breadcrumbs[format=list]") 58 | end 59 | 60 | test "renders as ordered list" do 61 | @breadcrumbs.add "Home", "/" 62 | html = Nokogiri::HTML(@breadcrumbs.render(format: "ordered_list")) 63 | 64 | refute_nil html.at("ol.breadcrumbs") 65 | end 66 | 67 | test "renders as list with custom attributes" do 68 | @breadcrumbs.add "Home", "/", class: "home" 69 | html = Nokogiri::HTML(@breadcrumbs.render(id: "breadcrumbs", class: "top")) 70 | 71 | refute_nil html.at("ul.top#breadcrumbs") 72 | end 73 | 74 | test "renders as list add items" do 75 | @breadcrumbs.add "Home", "/", class: "home" 76 | @breadcrumbs.add "About", "/about", class: "about" 77 | @breadcrumbs.add "People" 78 | 79 | html = Nokogiri::HTML(@breadcrumbs.render) 80 | items = html.search("li") 81 | 82 | assert_equal 3, items.count 83 | 84 | assert_equal "first item-0", items[0]["class"] 85 | assert_equal "Home", items[0].inner_text 86 | 87 | link = items[0].at("a") 88 | 89 | assert_equal "home", link["class"] 90 | assert_equal "/", link["href"] 91 | 92 | assert_equal "item-1", items[1]["class"] 93 | assert_equal "About", items[1].inner_text 94 | 95 | link = items[1].at("a") 96 | 97 | assert_equal "about", link["class"] 98 | assert_equal "/about", link["href"] 99 | 100 | assert_equal "last item-2", items[2]["class"] 101 | assert_equal "People", items[2].inner_text 102 | assert_nil items[2].at("a") 103 | refute_nil items[2].at("span") 104 | end 105 | 106 | test "renders inline" do 107 | @breadcrumbs.add "Home", "/", class: "home" 108 | html = Nokogiri::HTML(@breadcrumbs.render(format: "inline")) 109 | 110 | assert_nil html.at("ul.breadcrumbs") 111 | end 112 | 113 | test "renders inline add items" do 114 | @breadcrumbs.add "Home", "/", class: "home" 115 | @breadcrumbs.add "About", "/about", class: "about" 116 | @breadcrumbs.add "People" 117 | 118 | html = @breadcrumbs.render(format: "inline") 119 | html = Nokogiri::HTML("
#{html}
") 120 | separator = "»" 121 | 122 | items = html.search("div *") 123 | 124 | assert_equal 5, items.count 125 | 126 | assert_equal "a", items[0].name 127 | assert_equal "home first item-0", items[0]["class"] 128 | assert_equal "Home", items[0].inner_text 129 | assert_equal "/", items[0]["href"] 130 | 131 | assert_equal "span", items[1].name 132 | assert_equal "separator", items[1]["class"] 133 | assert_equal separator, items[1].inner_text 134 | 135 | assert_equal "a", items[2].name 136 | assert_equal "about item-1", items[2]["class"] 137 | assert_equal "About", items[2].inner_text 138 | assert_equal "/about", items[2]["href"] 139 | 140 | assert_equal "span", items[3].name 141 | assert_equal "separator", items[3]["class"] 142 | assert_equal separator, items[3].inner_text 143 | 144 | assert_equal "span", items[4].name 145 | assert_equal "last item-2", items[4]["class"] 146 | assert_equal "People", items[4].inner_text 147 | end 148 | 149 | test "renders inline with custom separator" do 150 | @breadcrumbs.add "Home", "/", class: "home" 151 | @breadcrumbs.add "People" 152 | 153 | html = Nokogiri::HTML(@breadcrumbs.render(format: "inline", separator: "|")) 154 | 155 | assert_equal "|", html.at("span.separator").inner_text 156 | end 157 | 158 | test "renders original text when disabling_translation" do 159 | @breadcrumbs.add :home, nil, i18n: false 160 | @breadcrumbs.add :people 161 | 162 | html = Nokogiri::HTML(@breadcrumbs.render) 163 | 164 | items = html.search("li") 165 | 166 | assert_equal "home", items[0].inner_text 167 | assert_equal "Nosso time", items[1].inner_text 168 | end 169 | 170 | test "renders internationalized text using default scope" do 171 | @breadcrumbs.add :home 172 | @breadcrumbs.add :people 173 | 174 | html = Nokogiri::HTML(@breadcrumbs.render) 175 | 176 | items = html.search("li") 177 | 178 | assert_equal "Página inicial", items[0].inner_text 179 | assert_equal "Nosso time", items[1].inner_text 180 | end 181 | 182 | test "renders scope as text for missing scope" do 183 | @breadcrumbs.add :contact 184 | @breadcrumbs.add "Help" 185 | 186 | html = Nokogiri::HTML(@breadcrumbs.render) 187 | items = html.search("li") 188 | 189 | assert_equal "contact", items[0].inner_text 190 | assert_equal "Help", items[1].inner_text 191 | end 192 | 193 | test "extends action controller" do 194 | methods = ActionController::Base.instance_methods 195 | 196 | assert(methods.include?(:breadcrumbs) || methods.include?("breadcrumbs")) 197 | end 198 | 199 | test "escapes text when rendering inline" do 200 | @breadcrumbs.add "" 201 | html = Nokogiri::HTML(@breadcrumbs.render(format: "inline")) 202 | 203 | assert_empty html.search("script") 204 | end 205 | 206 | test "escapes text when rendering list" do 207 | @breadcrumbs.add "" 208 | html = Nokogiri::HTML(@breadcrumbs.render) 209 | 210 | assert_empty html.search("script") 211 | end 212 | 213 | test "renders dots as breadcrumb items" do 214 | @breadcrumbs.add "." 215 | @breadcrumbs.add ".." 216 | @breadcrumbs.add "..." 217 | @breadcrumbs.add ". . ." 218 | html = Nokogiri::HTML(@breadcrumbs.render) 219 | 220 | items = html.search("li") 221 | 222 | assert_equal ".", items[0].inner_text 223 | assert_equal "..", items[1].inner_text 224 | assert_equal "...", items[2].inner_text 225 | assert_equal ". . .", items[3].inner_text 226 | end 227 | end 228 | -------------------------------------------------------------------------------- /test/support/pt-BR.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pt-BR: 3 | people: "Nosso time" 4 | 5 | breadcrumbs: 6 | home: "Página inicial" 7 | about: "Sobre" 8 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "simplecov" 4 | SimpleCov.start 5 | 6 | require "bundler/setup" 7 | require "minitest/utils" 8 | require "minitest/autorun" 9 | require "cgi" 10 | require "nokogiri" 11 | require "action_controller" 12 | 13 | require "breadcrumbs" 14 | 15 | I18n.load_path << "#{File.dirname(__FILE__)}/support/pt-BR.yml" 16 | I18n.locale = "pt-BR" 17 | --------------------------------------------------------------------------------