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 `[:get, :head]`

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 = [:get, :head].freeze
hoppergee marked this conversation as resolved.
Show resolved Hide resolved

# 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
10 changes: 7 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: Loaf.configuration.http_verbs)
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
return false unless http_verbs == :all || http_verbs.any? {|verb| request.try("#{verb}?")}
hoppergee marked this conversation as resolved.
Show resolved Hide resolved

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

Expand Down
48 changes: 48 additions & 0 deletions spec/integration/breadcrumb_trail_spec.rb
Expand Up @@ -70,4 +70,52 @@
)
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
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
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

click_on "Save & Next" # PATCH
expect(page).to have_selector("h1", text: "Step 4")
page.within '#breadcrumbs .selected' do

Choose a reason for hiding this comment

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

Style/StringLiterals: Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.

expect(page.html).to include('<a href="/onboard/step/4">Step 4</a>')
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
29 changes: 29 additions & 0 deletions spec/rails_app/app/controllers/onboard_controller.rb
@@ -0,0 +1,29 @@
class OnboardController < ApplicationController
hoppergee marked this conversation as resolved.
Show resolved Hide resolved

hoppergee marked this conversation as resolved.
Show resolved Hide resolved
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: [:get]
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
render "step1"
when "2"
breadcrumb "Step 2", onboard_step_path(step: 2), match: :exact, http_verbs: [:get, :post]

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. [95/80]
Style/SymbolArray: Use %i or %I for an array of symbols.

render "step2"
when "3"
breadcrumb "Step 3", onboard_step_path(step: 3), match: :exact, http_verbs: [:get, :put]

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. [94/80]
Style/SymbolArray: Use %i or %I for an array of symbols.

render "step3"
when "4"
breadcrumb "Step 4", onboard_step_path(step: 4), match: :exact, http_verbs: [:get, :patch]

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. [96/80]
Style/SymbolArray: Use %i or %I for an array of symbols.

render "step4"
when "5"
breadcrumb "Step 5", onboard_step_path(step: 5), match: :exact, http_verbs: [:get, :delete]

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. [97/80]
Style/SymbolArray: Use %i or %I for an array of symbols.

render "step5"
when "6"
breadcrumb "Step 6", onboard_step_path(step: 6), match: :exact, http_verbs: :all

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. [86/80]

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 %>
2 changes: 2 additions & 0 deletions spec/rails_app/app/views/onboard/step3.html.erb
@@ -0,0 +1,2 @@
<h1>Step 3</h1>
<%= button_to "Save & Next", onboard_step_path(step: 4), method: :patch %>
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>
7 changes: 7 additions & 0 deletions spec/rails_app/config/routes.rb
Expand Up @@ -4,4 +4,11 @@
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'
patch '/onboard/step/:step', to: 'onboard#setup'
put '/onboard/step/:step', to: 'onboard#setup'
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
delete '/onboard/step/:step', to: 'onboard#setup'
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
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
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
end
Capybara.default_driver = :rack_test
Capybara.default_selector = :css
28 changes: 26 additions & 2 deletions spec/support/dummy_view.rb
Expand Up @@ -3,9 +3,29 @@
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
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
def get?
true
_request_method == nil ? true : _request_method == "GET"
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
end

def post?
_request_method == nil ? false : _request_method == "POST"
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
end

def put?
_request_method == nil ? false : _request_method == "PUT"
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
end

def patch?
_request_method == nil ? false : _request_method == "PATCH"
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
end

def delete?
_request_method == nil ? false : _request_method == "DELETE"
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
end

def head?
_request_method == nil ? false : _request_method == "HEAD"
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
end
end
def request
Expand Down Expand Up @@ -51,4 +71,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: [:get, :head]
})
end

Expand Down
27 changes: 27 additions & 0 deletions spec/unit/view_extensions/breadcrumb_trail_spec.rb
Expand Up @@ -280,4 +280,31 @@
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: [:get, :post])
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
view.set_path("/posts")
view.set_request_method(:post)

expect(view.breadcrumb_trail.map(&:to_a)).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)

expect(view.breadcrumb_trail.map(&:to_a)).to eq([["posts", "/posts", false]])
hoppergee marked this conversation as resolved.
Show resolved Hide resolved
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)

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