Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add :http_verbs option #50

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ tmp
*.o
*.a
mkmf.log
.byebug_history
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2.7.3
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ rails generate loaf:install
* [2.1.1 controller](#211-controller)
* [2.1.2 view](#212-view)
* [2.1.3 :match](#213-match)
* [2.1.4 :request_methods](#214-request_methods)
* [2.2 breadcrumb_trail](#22-breadcrumb_trail)
* [3. Configuration](#3-configuration)
* [4. Translation](#4-translation)
Expand Down Expand Up @@ -207,6 +208,33 @@ To make a breadcrumb current based on the query parameters do:
breadcrumb "Posts", posts_path(order: :desc), match: {order: :desc}
```

#### 2.1.4 :request_methods

**Loaf** allows you to match on multiple HTTP methods in order to make a breadcrumb current with the `:request_methods` option.

The `:request_methods` key accepts `:all` or an array with following values:

* `:get`
* `:post`
* `:put`
* `:patch`
* `:delete`
* `:head`
* `:options`
* `:link`
* `:unlink`
* `:trace`

It's defaults to `%i[get head]`

Here are some examples:

```ruby
request_methods: %i[get head]
request_methods: %i[post get head]
request_methods: :all
```

### 2.2 breadcrumb_trail

In order to display breadcrumbs use the `breadcrumb_trail` view helper. It accepts optional argument of configuration options and can be used in two ways.
Expand Down
5 changes: 4 additions & 1 deletion lib/loaf/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ module Loaf
class Configuration
VALID_ATTRIBUTES = [
:locales_path,
:match
:match,
:request_methods
].freeze

attr_accessor(*VALID_ATTRIBUTES)
Expand All @@ -13,6 +14,8 @@ class Configuration

DEFAULT_MATCH = :inclusive

DEFAULT_REQUEST_METHODS = %i[get head].freeze

# Setup this configuration
#
# @api public
Expand Down
3 changes: 3 additions & 0 deletions lib/loaf/crumb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ class Crumb

attr_reader :match

attr_reader :request_methods

def initialize(name, url, options = {})
@name = name || raise_name_error
@url = url || raise_url_error
@match = options.fetch(:match, Loaf.configuration.match)
@request_methods = options.fetch(:request_methods, Loaf.configuration.request_methods)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metrics/LineLength: Line is too long. [92/80]

freeze
end

Expand Down
21 changes: 18 additions & 3 deletions lib/loaf/view_extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ def breadcrumb_trail(options = {})
_breadcrumbs.each do |crumb|
name = title_for(crumb.name)
path = url_for(_expand_url(crumb.url))
current = current_crumb?(path, options.fetch(:match) { crumb.match })
current = current_crumb?(
path,
options.fetch(:match) { crumb.match },
request_methods: options.fetch(:request_methods) { crumb.request_methods }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metrics/LineLength: Line is too long. [84/80]

)

yield(Loaf::Breadcrumb[name, path, current])
end
Expand All @@ -65,8 +69,8 @@ def breadcrumb_trail(options = {})
# the pattern to match on
#
# @api public
def current_crumb?(path, pattern = :inclusive)
return false unless request.get? || request.head?
def current_crumb?(path, pattern = :inclusive, request_methods: Loaf.configuration.request_methods)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metrics/LineLength: Line is too long. [103/80]

return false unless match_request_methods(request_methods)

origin_path = URI::DEFAULT_PARSER.unescape(path).force_encoding(Encoding::BINARY)

Expand Down Expand Up @@ -128,5 +132,16 @@ def _expand_url(url)
url
end
end

# Check if the HTTP request methods are allowed
#
# @retun [Boolean]
#
# @api private
def match_request_methods(request_methods)
return true if request_methods == :all

request_methods.any? { |method| request.try("#{method}?") }
end
end # ViewExtensions
end # Loaf
98 changes: 74 additions & 24 deletions spec/integration/breadcrumb_trail_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,66 +8,116 @@
it "shows root breadcrumb" do
visit root_path

page.within '#breadcrumbs .selected' do
expect(page.html).to include('<a href="/">Home</a>')
within "#breadcrumbs .selected" do
expect(page).to have_link(href: "/", text: "Home")
end
end

it "inherits controller breadcrumb and adds index action breadcrumb" do
visit posts_path

page.within '#breadcrumbs' do
expect(page.html).to include('<a href="/">Home</a>')
within "#breadcrumbs" do
expect(page).to have_link(href: "/", text: "Home")
end
page.within '#breadcrumbs .selected' do
expect(page.html).to include('<a href="/posts">All Posts</a>')
within "#breadcrumbs .selected" do
expect(page).to have_link(href: "/posts", text: "All Posts")
end
end

it 'filters out controller breadcrumb and adds new action breadcrumb' do
it "filters out controller breadcrumb and adds new action breadcrumb" do
visit new_post_path

page.within '#breadcrumbs' do
expect(page).to_not have_content('Home')
expect(page).to have_content('New Post')
within "#breadcrumbs" do
expect(page).to_not have_content("Home")
expect(page).to have_content("New Post")
end
end

it "adds breadcrumb in view with path variable" do
visit post_path(1)

page.within '#breadcrumbs .selected' do
expect(page.html).to include('<a href="/posts/1">Show Post in view</a>')
within "#breadcrumbs .selected" do
expect(page).to have_link(href: "/posts/1", text: "Show Post in view")
end
end

it 'is current when forced' do
it "is current when forced" do
visit new_post_path

expect(page.current_path).to eq(new_post_path)
page.within '#breadcrumbs' do
expect(page).to have_selector('li.selected', count: 2)
expect(page.html).to include('<a href="/posts">All</a>')
expect(page.html).to include('<a href="/posts/new">New Post</a>')
within "#breadcrumbs" do
expect(page).to have_selector("li.selected", count: 2)
expect(page).to have_link(href: "/posts", text: "All")
expect(page).to have_link(href: "/posts/new", text: "New Post")
end
end

it "allows for procs in name and url" do
visit post_comments_path(1)

page.within '#breadcrumbs .selected' do
expect(page.html).to include('<a href="/posts/1/comments">Post comments</a>')
within "#breadcrumbs .selected" do
expect(page).to have_link(href: "/posts/1/comments", text: "Post comments")
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
end
end

it "allows for procs in name and url without supplying the controller" do
visit post_comments_path(1)
visit post_comments_path(1, no_controller: true)

page.within "#breadcrumbs .selected" do
expect(page.html).to include(
'<a href="/posts/1/comments?no_controller=true">'\
"Post comments No Controller</a>"
within "#breadcrumbs .selected" do
expect(page).to have_link(
href: "/posts/1/comments?no_controller=true",
text: "Post comments No Controller"
)
end
end

it "match to Non-GET methods" do
piotrmurach marked this conversation as resolved.
Show resolved Hide resolved
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
visit onboard_path

expect(page).to have_selector("h1", text: "Onboard")
within "#breadcrumbs" do
expect(page).to have_content("Onboard")
end

click_link "Step 1" # GET
expect(page).to have_selector("h1", text: "Step 1")
within "#breadcrumbs" do
expect(page).to have_link(href: "/onboard", text: "Onboard")
end
within "#breadcrumbs .selected" do
expect(page).to have_link(href: "/onboard/step/1", text: "Step 1")
end

click_on "Save & Next" # POST
expect(page).to have_selector("h1", text: "Step 2")
within "#breadcrumbs .selected" do
expect(page).to have_link(href: "/onboard/step/2", text: "Step 2")
end

click_on "Save & Next" # PUT
expect(page).to have_selector("h1", text: "Step 3")
within "#breadcrumbs .selected" do
expect(page).to have_link(href: "/onboard/step/3", text: "Step 3")
end

if Rails.version >= "4.0.0"
click_on "Save & Next" # PATCH
expect(page).to have_selector("h1", text: "Step 4")
within "#breadcrumbs .selected" do
expect(page).to have_link(href: "/onboard/step/4", text: "Step 4")
end
end

click_on "Save & Next" # DELETE
expect(page).to have_selector("h1", text: "Step 5")
within "#breadcrumbs .selected" do
expect(page).to have_link(href: "/onboard/step/5", text: "Step 5")
end

click_on "Save & Next" # GET
expect(page).to have_selector("h1", text: "Step 6")
within "#breadcrumbs .selected" do
expect(page).to have_link(href: "/onboard/step/6", text: "Step 6")
end
end
end
6 changes: 3 additions & 3 deletions spec/integration/configuration_spec.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# encoding: utf-8

RSpec.describe 'setting configuration options' do
RSpec.describe "setting configuration options" do
it "contains 'selected' inside the breadcrumb markup" do
visit posts_path
page.within '#breadcrumbs' do
expect(page).to have_selector('.selected')
within "#breadcrumbs" do
expect(page).to have_selector(".selected")
end
end
end
6 changes: 4 additions & 2 deletions spec/rails_app/app/controllers/comments_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ class Article < Struct.new(:id, :title); end
class CommentsController < ApplicationController

breadcrumb lambda { |c| c.find_article(c.params[:post_id]).title },
lambda { |c| c.post_comments_path(c.params[:post_id]) }
lambda { |c| c.post_comments_path(c.params[:post_id]) },

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style/Lambda: Use the -> { ... } lambda literal syntax for single line lambdas.

match: :exact

breadcrumb -> { find_article(params[:post_id]).title + " No Controller" },
-> { post_comments_path(params[:post_id], no_controller: true) }
-> { post_comments_path(params[:post_id], no_controller: true) },
match: :exact

def index
end
Expand Down
36 changes: 36 additions & 0 deletions spec/rails_app/app/controllers/onboard_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

hoppergee marked this conversation as resolved.
Show resolved Hide resolved
class OnboardController < ApplicationController
breadcrumb "Onboard", :onboard_path, match: :exact

def setup
case params[:step]
when "1"
breadcrumb "Step 1", onboard_step_path(step: 1),
match: :exact, request_methods: %i[get]
render :step1
when "2"
breadcrumb "Step 2", onboard_step_path(step: 2),
match: :exact, request_methods: %i[get post]
render :step2
when "3"
breadcrumb "Step 3", onboard_step_path(step: 3),
match: :exact, request_methods: %i[get put]
render :step3
when "4"
breadcrumb "Step 4", onboard_step_path(step: 4),
match: :exact, request_methods: %i[get patch]
render :step4
when "5"
breadcrumb "Step 5", onboard_step_path(step: 5),
match: :exact, request_methods: %i[get delete]
render :step5
when "6"
breadcrumb "Step 6", onboard_step_path(step: 6),
match: :exact, request_methods: :all
render :step6
else
render :setup
end
end
end
piotrmurach marked this conversation as resolved.
Show resolved Hide resolved
3 changes: 3 additions & 0 deletions spec/rails_app/app/views/onboard/setup.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<h1>Onboard</h1>

<%= link_to "Step 1", onboard_step_path(step: 1) %>
2 changes: 2 additions & 0 deletions spec/rails_app/app/views/onboard/step1.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<h1>Step 1</h1>
<%= button_to "Save & Next", onboard_step_path(step: 2), method: :post %>
2 changes: 2 additions & 0 deletions spec/rails_app/app/views/onboard/step2.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<h1>Step 2</h1>
<%= button_to "Save & Next", onboard_step_path(step: 3), method: :put %>
6 changes: 6 additions & 0 deletions spec/rails_app/app/views/onboard/step3.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<h1>Step 3</h1>
<% if Rails.version >= "4.0.0" %>
<%= button_to "Save & Next", onboard_step_path(step: 4), method: :patch %>
<% else %>
<%= button_to "Save & Next", onboard_step_path(step: 5), method: :delete %>
<% end %>
2 changes: 2 additions & 0 deletions spec/rails_app/app/views/onboard/step4.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<h1>Step 4</h1>
<%= button_to "Save & Next", onboard_step_path(step: 5), method: :delete %>
2 changes: 2 additions & 0 deletions spec/rails_app/app/views/onboard/step5.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<h1>Step 5</h1>
<%= button_to "Save & Next", onboard_step_path(step: 6), method: :get %>
1 change: 1 addition & 0 deletions spec/rails_app/app/views/onboard/step6.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<h1>Step 6</h1>
11 changes: 10 additions & 1 deletion spec/rails_app/config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
RailsApp::Application.routes.draw do
root :to => 'home#index'
root to: "home#index"

resources :posts do
resources :comments
end

get "/onboard", to: "onboard#setup", as: :onboard
get "/onboard/step/:step", to: "onboard#setup", as: :onboard_step
post "/onboard/step/:step", to: "onboard#setup"
if Rails.version >= "4.0.0"
patch "/onboard/step/:step", to: "onboard#setup"
end
put "/onboard/step/:step", to: "onboard#setup"
delete "/onboard/step/:step", to: "onboard#setup"
end
1 change: 1 addition & 0 deletions spec/support/capybara.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

RSpec.configure do |c|
c.include Capybara::DSL, :file_path => /\bspec\/integration\//
c.filter_run_when_matching focus: true
end
Capybara.default_driver = :rack_test
Capybara.default_selector = :css
Loading