Reputation: 31
From a View file I pass attributes in a url:
%= link_to piece_path(@current_piece, file: file, rank: rank), method: :patch do %>
this gives urls like http://localhost:3030/pieces/%23?file=8&rank=8
I need to extract the value of file and rank from this url, to update the database fields (coordinates of a chess piece after a move).
In Controller I'm trying to use the Addressable gem:
def update
(some code)
current_piece.update_attributes(piece_params)
(more code)
end
private
def piece_params
uri = Addressable::URI.parse(request.original_url)
file = uri.query_values.first ## I don't know if "first"
rank = uri.query_values.last ## and "last" methods will work
params.require(:piece).permit({:file => file, :rank => rank})
end
When I inspect uri
I get: #<Addressable::URI:0x3fa7f21cc01c URI:http://test.host/pieces/2>
There is no hash of attributes trailing the url. Thus uri.query_values
returns nil
. I don't know how to mirror such thing in the test.
The error message:
1) PiecesController pieces#update should update the file and rank of the chess piece when moved
Failure/Error: file = uri.query_values.first
NoMethodError:
undefined method `first' for nil:NilClass
In Controller_spec:
describe "pieces#update" do
it "should update the file and rank of the chess piece when moved" do
piece = FactoryGirl.create(:piece)
sign_in piece.user
patch :update, params: { id: piece.id, piece: { file: 3, rank: 3}}
piece.reload
expect(piece.file).to eq 3
expect(piece.rank).to eq 3
end
I can't check if the logic works from the localhost browser (I don't have pieces objects at the moment so I run into errors). Also working on that.
My question regards the test; however if there are suggestions to extract the attributes from the url in different ways I'm all ears!
Upvotes: 0
Views: 551
Reputation: 31
button_to
with nested attributes works; in the view file:
<%= button_to piece_path(@current_piece), method: :patch, params: {piece: {file: file, rank: rank}} do %>
And keep it simple in the controller:
def piece_params
params.require(:piece).permit(:rank, :file)
end
Upvotes: 0
Reputation: 102055
You don't need to manually parse the request URI to get query params in Rails.
Rails is built on top of the Rack CGI interface which parses the request URI and request body and provides the parameters as the params hash.
For example if you have:
resources :things
class ThingsController < ApplicationController
def index
puts params.inspect
end
end
Requesting /things?foo=1&bar=2
would output something like:
{
foo: 1,
bar: 2,
action: "index",
controller: "things"
}
link_to method: :patch
uses JQuery UJS to let you use a <a>
element to send requests with other methods than GET. It does this by attaching a javascript handler that creates a form and sends it to the URI in the HREF attribute.
However unlike "normal forms" in rails the params are not nested:
<%= link_to piece_path(@current_piece, file: file, rank: rank), method: :patch do %>
Will give the following params hash:
{
file: 1,
rank: 2
}
Not
{
piece: {
file: 1,
rank: 2
}
}
If you want nested keys you would have to provide the params as:
<%= link_to piece_path(@current_piece, "piece[file]" => file, "piece[rank]" => rank), method: :patch do %>
Upvotes: 1
Reputation: 6942
If your URL is http://localhost:3030/pieces/%23?file=8&rank=8
you should be able to do:
def piece_params
params.require(:piece).permit(:rank, :file)
end
and then access them in your action via params[:rank]
and params[:file]
I usually use params[:file].present?
to make sure that the params are there before I try to assign the value. Something like this should work:
p = {}
if params[:rank].present?
p[:rank] = params[:rank]
end
if params[:file].present?
p[:file] = params[:file]
end
current_piece.update_attributes(p)
FWIW, you probably shouldn't use the URL string to pass params to a PATCH/PUT
request. You might consider passing them via a form or something.
Upvotes: 0