Routes configuration in ruby on rails - ruby-on-rails-4

I want to know the difference between them.
get "public/show"
get "public#show"
get "show/:something", :to => "public#show"
Thanks,
Mezbah

1) public/show
Since you do not specify a controller or action, Rails uses it's convention to map the URL "public/show" to the show action of the PublicController.
2) public#show
This is not a valid route.
3) show/:something
You define a parameter called "something" in the URL. This means that a URL such as "show/dog" and "show/cat" will call the show action of the PublicController. You can get the parameter as a string ("cat" or "dog) by using params[:something] in the controller.
The Rails documentation is really good. A good source of more information on routing: http://guides.rubyonrails.org/routing.html

Related

how to get $formBuilder->getForm() for a webform

I am trying to display my webform in a modal (including error/validation messages inside the modal). I am following this approach for a custom drupal form which is not a webform. I am wondering whether this is possible at all with webforms and in case it is, what I need to take into account to make it run?
The first problem I am having is the following code line from the example's TeacherContactController.php
$modal_form = $this->formBuilder->getForm('Drupal\tl_session\Form\TeacherForm');
When I try to use this in my own controller, I cannot get the webform's proper namespace + id. When I check my webform with devel it says:
[__CLASS__] => Drupal\webform\Entity\Webform
[id] => add_news_webform
But I get a "The form argument Drupal\webform\Entity\Webform\webform-submission-add_news_webform-form is not a valid form" error. I tried many things, but did not succeed.
How can I get the webform with formBuilder and what else am I missing here (like webform specific ajax settings etc.)?
You're trying to hard-code the namespace + ID in a string?
That'd be my first guess as to your problem
try
$modal_form = $this->formBuilder->getForm(Drupal\tl_session\Form\TeacherForm::class);
Or possibly
$modal_form = $this->formBuilder->getForm(\Drupal\tl_session\Form\TeacherForm::class);
If you enable the webform_devel.module you can use the API tab which provides some example code. (/webform/contact/api)
#see \Drupal\webform\WebformSubmissionForm::submitWebformSubmission

Ruby on Rails choosing wrong controller action

Today I came across some strange (and very inconvenient) Ruby on Rails behavior that even persistent combing of the net did not yield a satisfying answer to.
Note: I translated the method and route names to be easier to read in English, and hope I did not introduce any inconsistencies.
Situation
Environment
Ruby on Rails 4.2.0 executing under Ruby 2.0 (also tested under Ruby 2.2.0)
Relevant Code
consider a controller with these actions, among others:
class AssignmentsController < ApplicationController
def update
...
end
def takeover_confirmation
...
end
end
routes.rb
Since I use a lot of manually defined routes, I did not use resources in routes.rb. The routes in question are defined as follows:
...
post 'assignments/:id' => 'assignments#update', as: 'assignment'
post 'assignments/takeover_confirmation' => 'assignments#takeover_confirmation'
...
The relevant output of rake routes:
assignment POST /assignments/:id(.:format) assignments#update
assignments_takeover_confirmation POST /assignments/takeover_confirmation(.:format) assignments#takeover_confirmation
Problem
When I do a POST to the assignments_takeover_confirmation_path, rails routes it to the update method instead. Server log:
Started POST "/assignments/takeover_confirmation" for ::1 at ...
Processing by AssignmentsController#update as HTML
Mitigation
If I put the update route definition after the takeover_confirmation one, it works as intended (didn't check a POST to update though).
Furthermore, after writing all this I found out I used the wrong request type for the update method in routes.rb (POST instead of PATCH). Doing this in routes.rb does indeed solve my problem:
patch 'assignments/:id' => 'assignments#update', as: 'assignment'
However, even when defining it as POST, Rails should not direct a POST request to the existing path "/assignments/takeover_confirmation" to a completely different action, should it?
I fear the next time I use two POST routes for the same controller it will do the same thing again.
It seems I have a severe misconception of Rails routing, but cannot lay my finger on it...
Edit: Solution
As katafrakt explained, the above request to /assignments/takeover_confirmation matched the route assignments/:id because Rails interpreted the "takeover_confirmation" part as string and used it for the :id parameter. Thus, this is perfectly expected behavior.
Working Example
For the sake of completeness, here is a working (if minimalistic) route-definition that does as it should, inspired by Chris's comment:
resources :assignments do
collection do
post 'takeover_confirmation'
end
end
In this example, only my manually created route is explicitly defined. The routes for update, show, etc. (that I defined manually at first) are now implicitly defined by resources: :assignments.
Corresponding excerpt from rake routes:
...
takeover_confirmation_assignments POST /assignments/takeover_confirmation(.:format) assignments#takeover_confirmation
...
assignment GET /assignments/:id(.:format) assignments#show
PATCH /assignments/:id(.:format) assignments#update
PUT /assignments/:id(.:format) assignments#update
DELETE /assignments/:id(.:format) assignments#destroy
....
Thanks for the help!
However, even when defining it as POST, Rails should not direct a POST request to the existing path "/assignments/takeover_confirmation" to a completely different action, should it?
It should. Rails routing is matched in exact same order as defined in routes.rb file (from top to bottom). So if it matches a certain rule (and /assignments/takeover_confirmation matches assignments/:id rule) it stops processing the routing.
This behaviour is simple and efficient. I imagine that any kind of "smart" matching the best route would result in cumbersome and unexpected results.
BTW that's why catch-all route used to be defined at the very bottom of routing file.

Proper way to seed routes for view spec

I am testing my views with RSpec. Due to some changes, there is now a call to url_for in the view and all the spec that I wrote for show actions are failing:
No route matches {:controller=>"events", :action=>"show"}
The :id part is missing and because of that the call fails.
(I know that I can just stub that failing method call)
The only thing I found is pretty old and looks like a bad workaround. Also the RSpec documentation does not show something helpful.
Is there a proper way to tell RSpec that it should be on something like events/123?
Some more context:
In the view
I call a helper, something like this:
def some_helper_method
url_for(only_path: false)} + "something_else"
end
The call to that method fails with
Failure/Error: render
ActionView::Template::Error:
No route matches {:controller=>"events", :action=>"show"}
I'm currently fixing it by stubbing the call to that method view.stub(some_helper_method: 'SOME_URL')
Okay, I think I found your answer. I cloned your repo and debug a lot =P until I found that you need to infer the desired parameters in your view spec, since rspec-rails only provides the :controller parameter for the view being rendered. Well, in an attempt to understand better what the issue is, I found this explanation in the rspec-rails documentation:
View specs infer the controller name and path from the path to the view template. e.g. if the template is "events/index.html.erb" then:
controller.controller_path == "events"
controller.request.path_parameters[:controller] == "events"
This means that most of the time you don't need to set these values. When spec'ing a partial that is included across different controllers, you may need to override these values before rendering the view
In other words, the previous link that you posted still works and is still the solution for the parameters "issue" in your view spec. You can follow the discussion about the topic in this PullRequest thread.
So, the conclusion is that if you really don't want to stub out your call to url_for helper (or the helper that is calling it), you need to supply the desired :id parameter since there's no route for any show action without an :id param (as pointed out by you). Our final result (tested) is indeed:
controller.request.path_parameters[:id] = event.id
If this a workaround ? I really don't think so, since rspec-rails itself is internally using this feature to provide the :controller param.
I'm sorry I couldn't be more helpful before, but I think that this closes all of our main doubts.
My opinion ? If you really don't need to make any assertions based on the url_for behavior (or any helper behavior for that matter), just stub it, since it is expected to already have been tested in isolation. Otherwise, use the :id param, and don't worry about it. It is not a workaround.
Cheers friend !
If you will face the id problem the try this in your route.rb
get "events/show/:id", :to=> "events#show"

Rails4: How do I change the way particular URL helpers works?

The goal
No log in screens!
A visitor to the site should be able to create a widget without logging in.
This widget is publically accessible and can be shared via a short URL.
To edit this widget, you need to know the longer, administration URL.
The show action should have a URL with a short token instead of an id:
widget_path(widget) # => /widget/abc123
The edit action should have a URL with a long token instead of an id:
widget_path(widget) # => /widget/abcdefghijklmnop123/edit
What I have so far:
Generating tokens
I'm using a before_create callback to generate two tokens, a token and an admin_token with SecureRandom.urlsafe_base64.
Then, to change the URL helpers from generating URLs with the id, I override the to_param method in the model to return the token:
def to_param
token
end
Now when I save a new record, a token gets generated and the url helpers return these:
widget_path(widget) # => /widget/abc123
edit_widget_path(widget) # => /widget/abc123/edit
The problem
I need the edit_widget_path helper to use the admin_token field.
I can't seem to find a way of doing this.
In an ideal solution, I would want the _url versions of these to also work and they should be available in the usual places (controllers and views).
The closest I have found is to create custom _path and _url methods in ApplicationController, but this doesn't seem right.
Open to suggestions for how to achieve this.
Is there a way to use Rails' existing mechanism for generating URL helpers?
I hope that makes sense, feel free to ask for clarification.
Thank you!
I don't know of any rails mechanism that could handle that except inheritance. You could implement a subclass and override the to_param method there. I don't think that this is worth doing so, since you just want to handle 1 route here. I think I would just create a helper method to handle that case.
Another hint here: You could use the same mechanism that GIT uses. Create a UUID (long version) and use the first X digits to make the public url, just the full UUID is secret. This works in GIT 99,9% of the time without collisions, so it should work for you as well.

stubbing templates in RSpec functional tests

So I'm working on a gem that provides helpers for use with the Jeditable jQuery plugin, called jeditable-rails. These helpers are essentially writing javascript that create forms.
I am looking to stub templates within the request/controller specs, doing something like this:
gadget = Gadget.create!(:name => 'foo')
stub_template 'gadgets/edit.html.erb' => "<%= editable_field(#gadget, :name) %>"
# using Capybara
visit edit_gadget_path(gadget)
# fill out form, submit, etc.
When I run this in a request spec, I get the error undefined method 'stub_template'. Is it possible to use stub_template in request specs, or is there a different way to go about it?
This is Ruby, so it's probably possible, but it's not a given.
RSpec request specs delegate most of their work to Rails integration tests, which support multiple requests and, therefore, multiple controllers/views. stub_template requires access to a view instance, which we don't have access to in request specs.