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
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -22,3 +22,4 @@ tmp
*.o
*.a
mkmf.log
.byebug_history
28 changes: 28 additions & 0 deletions README.md
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 :http_verbs](#214-http_verbs)
* [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 :http_verbs

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

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

* `:get`
* `:post`
* `:put`
* `:patch`
* `:delete`
* `:head`
piotrmurach marked this conversation as resolved.
Show resolved Hide resolved
* `:options`
* `:link`
* `:unlink`
* `:trace`

Its default value is `%i[get head]`
hoppergee marked this conversation as resolved.
Show resolved Hide resolved

For example:
hoppergee marked this conversation as resolved.
Show resolved Hide resolved

```ruby
http_verbs: %i[get head]
http_verbs: %i[post get head]
http_verbs: :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
3 changes: 3 additions & 0 deletions lib/loaf/configuration.rb
Expand Up @@ -3,6 +3,7 @@
module Loaf
class Configuration
VALID_ATTRIBUTES = [
:http_verbs,
:locales_path,
:match
].freeze
Expand All @@ -13,6 +14,8 @@ class Configuration

DEFAULT_MATCH = :inclusive

DEFAULT_HTTP_VERBS = %i[get head].freeze

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

attr_reader :match

attr_reader :http_verbs

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

Expand Down
22 changes: 19 additions & 3 deletions lib/loaf/view_extensions.rb
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 },
http_verbs: options.fetch(:http_verbs) { crumb.http_verbs }
)

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, http_verbs: nil)
return false unless match_http_verbs(http_verbs)

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

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

# Check if the HTTP verbs are allowed
#
# @retun [Boolean]
#
# @api private
def match_http_verbs(http_verbs)
http_verbs ||= Loaf.configuration.http_verbs
return true if http_verbs == :all

http_verbs.any? { |verb| request.try("#{verb}?") }
end
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
end # ViewExtensions
end # Loaf
50 changes: 50 additions & 0 deletions spec/integration/breadcrumb_trail_spec.rb
Expand Up @@ -70,4 +70,54 @@
)
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")
page.within "#breadcrumbs" do
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
expect(page).to have_content("Onboard")
end

click_link "Step 1" # GET
expect(page).to have_selector("h1", text: "Step 1")
page.within "#breadcrumbs" do
expect(page.html).to include('<a href="/onboard">Onboard</a>')
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
end
page.within "#breadcrumbs .selected" do
expect(page.html).to include('<a href="/onboard/step/1">Step 1</a>')
Copy link
Owner

Choose a reason for hiding this comment

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

Similar to previous comment please consider using capybara helper:

expect(page).to have_link("Step 1", href: "/onboard/step/1")

end

click_on "Save & Next" # POST
expect(page).to have_selector("h1", text: "Step 2")
page.within "#breadcrumbs .selected" do
expect(page.html).to include('<a href="/onboard/step/2">Step 2</a>')
end

click_on "Save & Next" # PUT
expect(page).to have_selector("h1", text: "Step 3")
page.within "#breadcrumbs .selected" do
expect(page.html).to include('<a href="/onboard/step/3">Step 3</a>')
end

if Rails.version >= "4.0.0"
click_on "Save & Next" # PATCH
expect(page).to have_selector("h1", text: "Step 4")
page.within '#breadcrumbs .selected' do
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
expect(page.html).to include('<a href="/onboard/step/4">Step 4</a>')
end
end

click_on "Save & Next" # DELETE
expect(page).to have_selector("h1", text: "Step 5")
page.within "#breadcrumbs .selected" do
expect(page.html).to include('<a href="/onboard/step/5">Step 5</a>')
end

click_on "Save & Next" # GET
expect(page).to have_selector("h1", text: "Step 6")
page.within "#breadcrumbs .selected" do
expect(page.html).to include('<a href="/onboard/step/6">Step 6</a>')
end
end
end
36 changes: 36 additions & 0 deletions spec/rails_app/app/controllers/onboard_controller.rb
@@ -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, http_verbs: %i[get]
render :step1
when "2"
breadcrumb "Step 2", onboard_step_path(step: 2),
match: :exact, http_verbs: %i[get post]
render :step2
when "3"
breadcrumb "Step 3", onboard_step_path(step: 3),
match: :exact, http_verbs: %i[get put]
render :step3
when "4"
breadcrumb "Step 4", onboard_step_path(step: 4),
match: :exact, http_verbs: %i[get patch]
render :step4
when "5"
breadcrumb "Step 5", onboard_step_path(step: 5),
match: :exact, http_verbs: %i[get delete]
render :step5
when "6"
breadcrumb "Step 6", onboard_step_path(step: 6),
match: :exact, http_verbs: :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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1 @@
<h1>Step 6</h1>
11 changes: 10 additions & 1 deletion spec/rails_app/config/routes.rb
@@ -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
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
44 changes: 42 additions & 2 deletions spec/support/dummy_view.rb
Expand Up @@ -3,9 +3,45 @@
class DummyView < ActionView::Base
module FakeRequest
class Request
attr_accessor :path, :fullpath, :protocol, :host_with_port
attr_accessor :path,
:fullpath,
:protocol,
:host_with_port,
:_request_method
def get?
true
return true if _request_method.nil?

_request_method == "GET"
end

def post?
return false if _request_method.nil?

_request_method == "POST"
end

def put?
return false if _request_method.nil?

_request_method == "PUT"
end

def patch?
return false if _request_method.nil?

_request_method == "PATCH"
end

def delete?
return false if _request_method.nil?

_request_method == "DELETE"
end

def head?
return false if _request_method.nil?

_request_method == "HEAD"
end
end
def request
Expand Down Expand Up @@ -51,4 +87,8 @@ def set_path(path)
request.protocol = "http://"
request.host_with_port = "www.example.com"
end

def set_request_method(method)
request._request_method = method.upcase.to_s
end
end
3 changes: 2 additions & 1 deletion spec/unit/configuration_spec.rb
Expand Up @@ -19,7 +19,8 @@
config = Loaf::Configuration.new
expect(config.to_hash).to eq({
locales_path: "/",
match: :inclusive
match: :inclusive,
http_verbs: %i[get head]
})
end

Expand Down
30 changes: 30 additions & 0 deletions spec/unit/view_extensions/breadcrumb_trail_spec.rb
Expand Up @@ -280,4 +280,34 @@
expect(view.breadcrumb_trail.map(&:url)).to eq(%w[/ /posts /posts/1])
expect(view.breadcrumb_trail(match: :exact).map(&:current?)).to eq([false, false, true])
end

it "match current path with :http_verbs" do
view = DummyView.new
view.breadcrumb("posts", "/posts", http_verbs: %i[get post])
view.set_path("/posts")
view.set_request_method(:post)

trail = view.breadcrumb_trail.map(&:to_a)
expect(trail).to eq([["posts", "/posts", true]])
end

it "fail to match current path with :http_verbs" do
view = DummyView.new
view.breadcrumb("posts", "/posts")
view.set_path("/posts")
view.set_request_method(:post)

trail = view.breadcrumb_trail.map(&:to_a)
expect(trail).to eq([["posts", "/posts", false]])
end

it "match current path with :http_verbs => :all" do
view = DummyView.new
view.breadcrumb("posts", "/posts", http_verbs: :all)
view.set_path("/posts")
view.set_request_method(:delete)

trail = view.breadcrumb_trail.map(&:to_a)
expect(trail).to eq([["posts", "/posts", true]])
end
end