RSpec controller example param with periods - ruby-on-rails

In my controller spec, when I execute the line:
post :register_pass, :device_id => 'DEV1C3', :pass_type_id => 'pass.com.example.GSPassType', :serial_no => '5ER14L'
I get an ActionController::RoutingError:
Failure/Error: post :register_pass, :device_id => 'DEV1C3', :pass_type_id => 'pass.com.example.GSPassType', :serial_no => '5ER14L'
ActionController::RoutingError:
No route matches {:action=>"register_pass", :controller=>"purchases/passbook/registrations", :pass_type_id=>"pass.com.example.GSPassType", :serial_no=>"5ER14L", :device_id=>"DEV1C3"}
Even though rake routes | grep register_pass returns
register_pass POST /v1/devices/:device_id/registrations/:pass_type_id/:serial_no(.:format) {:action=>"register_pass", :controller=>"purchases/passbook/registrations"}
However, when I remove the periods in the :pass_type_id value, the post line listed above executes and the route is recognized (I verified this using rspec, and even curled it directly, putting a breakpoint in the expected controller action, the route works).
I tried using Rack::Utils.escape on the value with periods, but that failed too. I also tried manually changing the periods to their URL-encoded values, but rails didn't seem to decode them in the params hash.
Why don't periods work in this case?
And how can I get a value with periods passed in (without manually decoding it using String#gsub in the controller?

There are two problems you're encountering: 1) a period is a valid character in a URL sequence, so it's won't be replaced by Rack::Utils.escape or URI.escape, 2) Rails treats a period as a path separator to enable format parsing at the end of urls, e.g. .json, .xml, etc. (as defined in ActionController::Routing::SEPARATORS).
For this use case, I'd recommend adding constraints to this route that allow you to be permissive of any character for :post_id. I don't see what your route file looks like, but it could be something like this with the change:
post "/v1/devices/:device_id/registrations/:pass_type_id/:serial_no(.:format)" =>
"purchases/passbook/registrations#register_pass",
:constraints => { :pass_type_id => /.*/ }
Lots of great info on other ways to add this constraint in the Rails guide on routing

Related

What is the right Rails route for passing in a different parameter than id, to return a JSON User object? Controller method provided

EDIT: This is Rails 4
Rails code in the users_controller.rb file
def showobjectdata
#users = User.all
#user = User.find_by(:username => params[:username])
render :json => #user
end
I have been trying lots of routes, but (add the "localhost" part to the beginning of this URL) /users/showobjectdata/existingusername in my browser
returns null.
Please Note: I am able to render JSON data about all users or a specific user, if I look up the user some other way than passing in a parameter which is not an id in the browser's URL field. Like in the controller method I can specifically look up a user by a specific email address. And users/show/:id renders the JSON user data of that id, because I have defined the show controller method to render JSON user data (for now).
Here is an example of a route I tried in my routes.rb file:
match 'users/showobjectdata/:username', to: 'users#showobjectdata', via: [:get, :post]
I tried various combinations with plain GET, plain POST, nested parentheses, etc. I always get null except for plain POST which doesn't work.
Try this
match 'users/showobjectdata/:username', to: 'users#showobjectdata', via: [:get, :post], param: 'username'
This is the right answer.
Basically, my username parameter (firstname.lastname) was not being passed as a full string. It is was being passed as firstname instead of firstname.lastname, with the Rails application considering "." to be where the format parameter started ('lastname' was considered a format input in the passed in parameters). I saw these passed in parameters appear in my browser ironically only when I got another error trying something new (basically my application was not responding to 'respond_to |format|' in the 'showobjectdata' method when I tried it pretty randomly - this of course led to these parameters showing up at the bottom of the screen and the googling of a solution. Yes after getting this insight on the parameters, I skipped the respond_to way and once again just rendered the json user object directly as before, without differentiating between the HTML and JSON formats).
So, basically this is the right route that worked for me:
match 'users/showobjectdata/:username', to: 'users#showobjectdata', via: [:get, :post], :constraints => { :username => /[^\/]+/ }
The controller method as originally posted is fine!
Source for the ":constraints =>" part:
Why do routes with a dot in a parameter fail to match?

Why did Ruby on Rails' URL Helper put a period in my URL?

I have the following code in my view (RoR 4):
tbody
- #order_submissions.each do |order_submission|
tr
td = order_submission.id
td.table-actions
span = link_to "Show", order_submissions_path(order_submission.id)
td = order_submission.id
successfully displays as the ID number (533ab7337764690d6d000000)
But...
order_submissions_path(order_submission.id)
Creates a URL that comes out as:
order_submissions.533ab7337764690d6d000000
I want it to be
order_submissions/533ab7337764690d6d000000
Where did that period come from?
This is my route:
get 'order_submissions/:id' => 'order_submissions#show'
And when I run rake routes I get:
GET /order_submissions/:id(.:format) order_submissions#show
The (.:format) is probably what's messing it up but I don't know why. I just want it to put a slash in there.
If I change my code to this it fixes it:
span = link_to "Show", order_submissions_path + '/' + order_submission.id
But that's a really, really stupid workaround.
EDIT: Here are my routes:
get 'order_submissions' => 'order_submissions#index'
get 'order_submissions/new' => 'order_submissions#new'
post 'order_submissions' => 'order_submissions#create'
get 'order_submissions/:id' => 'order_submissions#show'
get 'order_submissions/:id/edit' => 'order_submissions#edit'
patch 'order_submissions/:id' => 'order_submissions#update'
get 'order_submissions/:id/delete' => 'order_submissions#delete'
delete 'order_submissions/:id' => 'order_submissions#destroy'
The order_submissions_path (plural) points to /order_submissions. It takes two arguments, the first being the format for the request (e.g. html). Your ID is being passed in for this argument, leading to the resulting URL you're seeing.
You actually want the singular path helper, order_submission_path, which accepts an ID as the first argument.
Because it should be a singular form:
order_submission_path(order_submission.id)
Not
order_submissions_path(order_submission.id)
order_submissions_path points onto index action. You can also remove id from the end.
UPDATE:
Just notice you route file. Do you have any resources defined there? The route you posted wouldn't generate any url_helper as you dind't specify route name (most likely this entry is obsolete, as I expect there is resources :order_submissions somewhere there as well).
You don't get a named route by default. The route you showed from rake routes doesn't list a named route, for example.
GET /order_submissions/:id(.:format) order_submissions#show
Normally, you'd see the named route in front of GET there.
So you can define it yourself and then your route will work:
get 'order_submissions/:id' => 'order_submissions#show', as: :order_submission
Notice the as: :order_submission bit. Now, order_submission_path(order_submission.id) will work. (Note: .id is superfluous if your order_submission responds to to_path and returns id.)
I'm guessing you have another route in your rake routes output that uses the named route you supplied and that doesn't use /:id. Perhaps your index route?

Rails routing: Treating hardcoded path as a parameter?

I'm new to Rails, and after reading some of the Rails Routing documentation, I still can't figure out why my path is being treated as such:
With the following examples, Rails complains that it is unable to find a Movie whose id is equal to "inception", even though I am trying to pass along an id parameter.
(The controller#action to which I am routing is of the resources-type show).
routes.rb
1: get 'movies/inception' => "movies#show", :id => 6
2: get 'movies/inception' => 'movies#show', defaults: {id: 6}
(These two lines are obviously tried separately.)
Error
1:Couldn't find Movie with id=inception
2: app/controllers/movies_controller.rb:16:inshow'
3: {"id"=>"inception"}
Why is the inception-part of the matched url treated as a variable even when I'm not prefixing it with : and how do I make a hardcoded url point to a object#show-action with a hardcoded id?
Looks like you taked into issue with routes priority:
Not all routes are created equally. Routes have priority defined by the order of appearance of the routes in the config/routes.rb file. The priority goes from top to bottom. The last route in that file is at the lowest priority and will be applied last. If no route matches, 404 is returned.
So, I suppose, that you had defined
resources :movies
before
get 'movies/inception' => "movies#show", :id => 6
get 'movies/inception' => 'movies#show', defaults: {id: 6}
And your routes were not overwritted.
Just see your last question. Uses this route form, instead of yours:
get 'movies/inception/(:id)' => 'movies#show', defaults: {id: 6}
My guess is you have a line something like
resources :movies
If this is the case, then it is creating the standard get show route of movies/:id
If this line is above your hard-coded route then it would take precedence. The lines at the top of the routes file take precedence over anything below it
A couple of things that may help you on your rails journey. From the command line, run
rake routes
That will spit out all available routes
Something you may want to consider if you are looking to do this:
Check out the friendly_id gem
"It allows you to create pretty URL’s and work with human-friendly strings as if they were numeric ids for ActiveRecord models." such as /movies/inception

Rails catch-all/globbing routes

I am using rails 3.0.14, and I am constructing routes.rb using the resourceful style. I'd like to have a wildcard route that catches all requests that do not match to any route stated.
What's the appropriate way to construct such a route?
put
match '*path' => 'your_controller#your_action'
at the end of the routes.rb file. This is important, since the routes are stepped through top down.
See also http://guides.rubyonrails.org/routing.html -> 3.10
For Rail 4, you need to specify the request type:
match "*path", to: "application#custom_action", via: :all
As others have said, put this at the very bottom of your routes file.
It is not mandatory to use exactly "path" in the match '*path' statement. You can set a whatever token there:
get "*string1"
or
get "*string2"
Rails will assign your real HTTP-query to the param named after your token, example:
get "*user" => "users#show"
in console:
Started GET "/john" ....
Processing by UsersController#show as HTML
Parameters: {"user"=>"john"}
You can use more than one asterisks, say get "*id*user". But in this case you will get some unpredictable result, because Rails processes 2 or more asterisks "in an intuitive way" - for more info see http://guides.rubyonrails.org/routing.html#route-globbing-and-wildcard-segments
In addition to #steel and #awenkhh, I recommend adding the following to that route's controller action
respond_to do |format|
format.html
# other formats you already support
format.all { render text: '' }
end
Otherwise, you'll wind up with some ActionView::MissingTemplate: Missing template errors for formats that you weren't expecting.
[rant]Particularly helpful for those people trying erroneous attack vectors around /wp-admin/css/wp-admin.css and the like. I seem to get about 100 requests for /wp-admin/* a day, from super annoying people who apparently would like me to get a more expensive Rollbar account plan.[/rant]

Check for the existence of multiple params

I'm creating an entry form that I want to only be accessible when three url params are in place: example.com/entries/new/2011/01/27 If someone tries to access any other url (i.e. example.com/entries/new or example.com/entries/new/2011/) I want Rails to set an :alert and bounce the user back to the index page.
Currently, I only have this code in my routes.rb match '/entries/new/:year/:month/:day' => 'entries#new'. What do I need to do to control the redirection if the proper params aren't in the URL? Would I check for each param in the controller and then perform a redirect_to, or is this something I can do from the routes.rb file exclusively? If it's the former, is there an easier way to check that all three params exist other than:
if params[:year].nil && params[:month].nil && params[:day].nil redirect_to ...
This route requires the presence of all three parameters:
match '/entries/new/:year/:month/:day' => 'entries#new'
With only that route, GET /entries/new will result in:
No route matches "/entries/new"
You can redirect from within routes.rb like this:
match '/entries' => 'entries#index'
match '/entries/new/:year/:month/:day' => 'entries#new'
match "/entries/new/(*other)" => redirect('/entries')
The second line matches paths where all three parameters are present. The third line matches all other cases of /entries/new using "route globbing", and does the redirect. Requests matched by the third line will not hit EntriesController#new.
Note: you may not need the first line if you've already defined a route to EntriesController#index -- but watch out for resources :entries, which will redefine index and new.
More info can be found in the guide Rails Routing From the Outside In. When using date parameters, constraints are a good idea (Section 4.2)

Resources