Rails router: Avoiding long helpers - ruby-on-rails

Consider the following snippet from router.rb
resource :user do
namespace :settings do
resource :access_filter
end
get 'settings'
end
This generates the URL's I want (user/settings/access_filter), but it produces an undesired directory structure and some long url helpers.
In this case, the helper becomes new_user_settings_access_filter and I would prefer just to have new_access_filter. And Rails expects the AccessFiltersController to reside in a module named Settings.
I would like a way to keep a flat directory structure, a flat url helper structure, but a "nested" URL structure. As long as no parameters are neccessary, I don't think my controllers and views needs to know that access filters are nested under the user model and I would like to retain the opportunity to move them to say options/access_filter without breaking the site.
Sugggestions on how to get there would be appreciated.

If you only want to prefix the path, you could avoid the nesting and any hassle involved by using a separate scope. Organizationally, this seems to make sense as well, since you effectively want to decouple the access filter routes from user.
scope "user/settings" do
resource :access_filter
end
# then if you wanted to change it later, it's simply
scope "options" do
resource :access_filter
end
scope is a lower level function actually used by namespace. It's far more flexible, and allows you to decorate paths without changing the controller lookup and route names, among other things.

Related

Rails named route with dynamic segments URL generation from model

Ruby on Rails 4.2+ only, please!
I've been looking all over for tips on how to make URLs pretty in Rails, and I'm struggling to see a solution I like.
What I want:
Hypothetical example: given Topic, Course, etc. models that have a bunch of fields (including URL-friendly slugs), I want to be able to
# ../routes.rb
# Match urls of the form /edu/material-engineering. These are read-only
# public URLs, not resources.
get 'edu/:slug', to: 'education#topic', as: :learn_topic
get 'edu/course/:id/slug', to: 'education#course', as: :learn_course
...
# I also have admin-only resource-oriented controllers for managing
# the content, but that's separate.
namespace :admin do
resource :topic
resource :course
...
end
# ../some_view.html.erb
# Generate URLS like this:
<%= link_to topic.name, learn_topic_path(topic) %>
<%= link_to course.name, learn_course_path(course) %>
What I don't want:
Messing with to_param. That's a dirty hack and completely breaks separation of concerns.
Resource/RESTful routing. There are no CRUD operations here other than "read."
link_to 'text', course_path(id: course.id, slug: course.slug). This completely defeats the purpose of not requiring views to know what params are required to generate a URL for a course.
EDIT: I know FriendlyId exists, but I'm precisely trying to understand how this sort of thing can be done and what the mechanics are, so that's not a solution for now.
There has to be a way to tell the named route helper topic_path(topic) to take the required parameters in the route (e.g, :slug, :id, whatever else) from the topic model object.
Anybody know? Thanks!
The best I've been able to come up with: just override the *_path helpers with my own implementation.
If you know a way to make the default helpers work, please chime in!
This problem boils down to one issue: the auto-generated *_path and *_url helpers don't give me the flexibility I want. What I want them to do is trivial, so without another option, I can just write my own:
module ApplicationHelper
def learn_topic_path(topic)
"/edu/#{topic.slug}"
end
...
end
Writing a few _path/_url helper overrides avoids all kinds of complication, and allows you to keep out of to_param, avoid including new plugins, etc.
One could probably go another step forward and generate the static components of the route from known routing rules, as well as infer what attributes one needed to extract from a model if the dynamic segment names line up to the model attribute names, but that starts to break down once you do more complicated things or add multiple models (e.g., 'edu/:topic_slug/:course_slug').
The big downside to doing this is that you now have to update routes in two places every time you change them: the route definition itself in routes.rb as well as the corresponding route helper in application_helper.rb. I can live with that for now.
You can use FriendlyId gem to achieve that.
Here's the link:
https://github.com/norman/friendly_id/blob/master/README.md
Let me know if you have questions.

Rails incorrectly generates route for a resource with :controller set

So here's the routes in question:
resources :subjects, path: 'library' do
resources :modules, controller: 'subject_modules'
end
When I write form_for [#subject, #subject_module] (with those two set to what you'd expect), Rails tries to generate "subject_subject_module_path".
When I remove the :path for the subjects resource, the generated helper remains the same (as expected).
What it should be is "subject_module_path"; I suspect the problem is that Rails looks at the controller for the modules resources and uses that instead of its actual path, i.e. it builds this helper:
subject_ + subject_module_ + path
From what I've gathered so far, it's pointing towards the possibility of a bug, but is it possible it's more something on my side or something intended by design?
For now, I suppose this is usable as a temporary fix:
form_for([#subject, #subject_module], url: subject_module_path(#subject, #subject_module))
I'm using Rails 3.1.3.
If this is a form (update or create) then your routes should be pointing to
subjects_module_path
which should generate the URL
/library/modules/
have you tried pointing your form at
form_for [:subjects, #subject_module]?
which will then end up at your subject_modules controller
To be honest, your naming is very confusing and probably not helping. Ideally subject_modules should just be modules.
This is something that occurs due to the design of Rails.
The following is what the Rails API docs say in relation to url_for:
Relying on named routes
Passing a record (like an Active Record or Active Resource) instead of
a Hash as the options parameter will trigger the named route for that
record. The lookup will happen on the name of the class. So passing a
Workshop object will attempt to use the workshop_path route. If you
have a nested route, such as admin_workshop_path you’ll have to call
that explicitly (it’s impossible for url_for to guess that route).
But saying explicitly declaring a helper is required when you have a "nested route" is in accurate. If the nesting occurs under a resource (without certain kinds of routing options, more on this later), Rails will be able to generate the path without any issues.
form_for appears to at some point, like url_for, call polymorphic_url in order to generate the URL to target, which in turn calls build_named_route_call.
You can see that build_named_route_call simply generates underscored_versions of the ModelNames passed in, and joins them together with underscores.
Bringing that back to my routing:
resources :subjects, path: 'library' do
resources :modules, controller: 'subject_modules'
end
Since the subjects resource is under /library/ by setting :path, its helpers remain as subjects_*, and thus Rails has no problems generating a URL for it when passed a Subject. The subject_modules resource (named as such since Rails reserves the name Module for models) however, has had its named helpers changed from its model name through the setting of :controller.
Mystery solved.

What is a "resource" in Rails?

Dumb question but I have some lingering confusion of what, exactly, a "resource" is in Rails. The term is used everywhere but I get a funny feeling it might be being used rather loosely. It's referenced in the model, the controller and, quite literally, in routes.rb.
Is it the specific route? For example, map.resources maps the 7 RESTful "resources". So an example of one resource would be the call to, say, the index action of a particular class's controller?!?
Is it a reference to the whole page/object being retrieved? or perhaps, more narrowly, a database table? or the row being retreived?
Is it something else?
Anyway, hopefully someone can set me straight...
Any object that you want users to be able to access via URI and perform CRUD (or some subset thereof) operations on can be thought of as a resource. In the Rails sense, it is generally a database table which is represented by a model, and acted on through a controller.
For example, you might have a User resource (with a users table in your DB). This is represented by a User model, is mapped to users_controller with map.resources :users (which then generates routes like /users (a collection of User resources) and /users/1 (a specific User resource).
You act upon those resources by using the appropriate HTTP method when making calls to those resources. POST to the resource collection (/users) creates a new record; GET retrieves a list of resources (/users) or a specific user (/users/1). PUT updates a specific user (/users/1/), and DELETE destroys that user. The URLs are the same, but the result (and controller action) may be different based on the HTTP verb. The idea, though is that /users/1 always means "I'm interacting with the User that has ID #1", regardless of the action.
Here's a good article discussing how most developers think that "Resource" is synonomous with the database table, the argument, I guess, being that mapping to the resource is mapping the controller to that database table (or, with ActiveResource, to another REST url).
Basically, I think a "resource" is "persisted data." map.resources maps the 7 RESTful actions to a particular suite of persisted data.
But I haven't thought about it too much in depth. Good question!
I think they probably mean it in the general web sense, i.e., Resource (Web):
the referent of any Uniform Resource Identifier
I don't think it has anything to do with database tables.
open your model folder, that is a hint of what resources you have!
example: users, pictures, comments...
A lot of people here say that resources refer to the database tables you have. It might be true sometimes but not necessarily true always. I could give you a lot of examples where you don't have a corresponding table in your database for a particular resource. Hence asssociating it with tables is rather wrong.
I would define a resource as a route which maps to related requests. So instead of declaring separate routes for the actions you want to do you can simply declare them using a resourceful route.In Rails, a resourceful route provides a mapping between HTTP requests and URLs to controller actions.
So say you define resources :users in config/routes.rb. You can now use a number of helpers to the controllers in your application like edit_user_path which returns users/edit .
Here's a good link: https://api.rubyonrails.org/v5.2.1/classes/ActionDispatch/Routing/Mapper/Resources.html
Which basically says: Resource routing allows you to quickly declare all of the common routes for a given resourceful controller. Instead of declaring separate routes for your index, show, new, edit, create, update and destroy actions, a resourceful route declares them in a single line of code:
resources :photos

Ruby on Rails - differentiating plural vs singular resource in a REST API

I'm working on building the URLs for my REST API before I begin writing any code. Rails REST magic is fantastic, but I'm slightly bothered the formatting of a URL such as:
http://myproject/projects/5
where Project is my resource and 5 is the project_id. I think if a user is looking to retrieve all of their projects, then a respective HTTP GET http://myproject/projects makes sense. However if they're looking to retrieve information on a singular resource, such as a project, then it makes sense to have http://myproject/project/5 vs http://myproject/projects/5. Is it best to avoid this headache, or do some of you share a similar concern and even better - have a working solution?
Rails (3) has a lot of conventions when it comes to singular vs plural. For example, model classes are always singular (Person), while the corresponding tables are always plural (people). (For example, Person.all maps to select * from people.)
For routes, there's a concept of a singular resource as well as a plural resource. So if you did resource :account then you would get paths like /account for the default path or /account/edit for a path to a form to edit the account. (Note that Rails uses /account with a PUT method to actually update the account. /account/edit is a form to edit the account, which is a separate resource from the account itself.) If you did resources :people, however, then you would get paths like /people, /people/1, and /people/1/edit. The paths themselves indicate whether there can only be one instance of a given type of resource, or whether there can be multiple instances distinguished by some type of identifier.
I agree, go with the flow. Consider how the URL forms a hierarchy.
The root of your website is where you start to access anything.
/projects/ narrows it down to only projects, not anything else. From projects you can do lots of things, /list, /index/, /export, etc... the /id limits things even further.
At each / the scope of what do becomes narrower, and I think it makes sense.
Further programming is all about arbitrary rules. Indexs starting at 1 vs 0, and so on. Anyone working with your urls will sort things out in short order.
There are cases where a singular path to a resource is helpful. If your resource ids are non-numeric user defined names then routing clashes are possible. Example:
/applications/new --> create a new application or show user's application named new?
In this situation you can choose to limit the user input to avoid the clash, or, this can be worked around by overwriting the default Rails 3 behavior:
class ActionDispatch::Routing::Mapper
module Resources
RESOURCE_OPTIONS << :singular_resource
class Resource
def member_scope
#options[:singular_resource] ? "#{singular}/:id" : "#{path}/:id"
end
def nested_scope
#options[:singular_resource] ? "#{singular}/:#{singular}_id" : "#{path}/:#{singular}_id"
end
end
end
end
Then when specifying a new resource route:
resources :applications, :singular_resource => true
Which will generate the routes:
GET /applications
GET /applications/new
POST /applications
GET /application/:id
GET /application/:id/edit
PUT /application/:id
DELETE /application/:id

Singular or plural controller and helper names in Rails

Is there any disadvantage to using singular names for controllers and helpers? Nothing seems to rely on this. It even seems helpers don't have to make the same choice about singular vs. plural as their corresponding controllers, at least according to my limited experimentation. Is that true?
Definitely plural.
With restful routing and a singular controller
Controller:
dog_controller.rb
Routes:
map.resources :dogs # => blows up
map.resources :dog # is ok, but...
dogs_path # => blows up
dog_path # => ok
Using a plural controller
Controller:
dogs_controller.rb
Routes:
map.resources :dogs
dogs_path # => ok
dog_path # => ok
rails generate controller --help has plural examples:
Example:
`rails generate controller CreditCards open debit credit close`
CreditCards controller with URLs like /credit_cards/debit.
Controller: app/controllers/credit_cards_controller.rb
Test: test/controllers/credit_cards_controller_test.rb
Views: app/views/credit_cards/debit.html.erb [...]
Helper: app/helpers/credit_cards_helper.rb
Using plural names for controllers is just a convention.
Plural names usually sound more natural (especially for controllers that are tied directly to a specific model: User -> Users, etc.), but you can use whatever you want.
As for helpers, all helpers are available for all controllers by default, so technically, how you name your helpers doesn't matter at all. It's just another convention to keep a controller's helper functions in a helper with the same name as the controller.
A Model is singular because it references a single object like User. A controller is plural because it is the controls (methods) for the collection of Users. How one names the routes is all up to that individual developer. I've never had a user complain that a URL for a web request is singular or plural. The end result to maintain a common convention for current and future contributors while serving quality page displays or the API requests for the end users.
You have a very complete explanation in the Rails guides:
http://edgeguides.rubyonrails.org/routing.html#resource-routing-the-rails-default
It is the Rails convention that one controller handles one model, whether one or more instances of that model can exist during runtime. However, you can have a Rails application where (some of) the controllers (and the associated views) are not associated with any particular model, but rather handle a more complex set of functionality. In this case, the automatic pluralization doesn't make any sense.
The Rails application I'm currently working on fits into this category, and it's simply an irritation to me that Rails expects that the identifiers I define as a singular in one place are then used in their plural forms in other places. For example, I might want to define something like this in config/routes.rb:
resource :dashboard, :only => [:show]
and then I want a controller DashboardController to display summary information about certain aspects of the application, gathering information from more than one database table. So here, Dashboard does not refer to any model of the application, and it would be just weird to have the controller's name be DashboardsController.
I found a good solution to the irritation of automatic pluralization in this answer. In short, edit file config/initializers/inflections.rb and add the words you don't want to be automatically pluralized to this definition:
ActiveSupport::Inflector.inflections do |inflect|
inflect.uncountable %w( dashboard foo bar baz )
end
If the controller is a resource then it must be plural...
For example
Controller
articles_controller.rb
Model
article.rb
But you can use singular controller names when you do not have corresponding models like
welcome_controller.rb
The naming convention of controllers in Rails favors pluralization of the last word in the controller's name, although it is not strictly required (e.g. ApplicationController).
For example, ClientsController is preferable to ClientController, SiteAdminsController is preferable to SiteAdminController or SitesAdminsController, and so on.
Following this convention will allow you to use the default route generators (e.g. resources, etc) without needing to qualify each :path or :controller, and will keep URL and path helpers' usage consistent throughout your application.
Ref: Controller Naming Convention-Rails Doc
I feel better when I use singular for Controller name
Using plurals just sounds better, and then if you have a controller that handles a singular resourse, ie user, then you can still name the url /user.
With helpers there is often no need to have a helper for every controller, and often there will be helper methods you can use ascorss multiple controllers and rather litter them all through your application helper you could put them in custom helpers instead like eg layout_helper or any other well named file.

Resources