Ruby on Rails Routing, Double Nested Slugs - ruby-on-rails

I'm trying to setup the basic routing and URLs of my application. I'm using the FriendlyId gem to have nicer looking URLs.
In my model design an Account has Users, and an Account has Farms. When a User signs in, I want to redirect them to myapp.com/account-name. This page should be an index page of that account's farms, along with some other options. When they click on a farm, I want the page to go to myapp.com/account-name/farm-name.
How do I do this? Is this just totally anti-RESTful to not include /account or /farms in the URL? If it is, then what can I do to have the url be myapp.com/account-name/farms/farm-name?
Right now I have it correctly showing myapp.com/account-name by having
get "/:id", to: "accounts#show", as: 'account'
but I don't think I can extend that to include farms. I think it may be solved with something like
resource :account do
resource :farms
end
But that shows myapp.com/account.account-name when I go to the account, and when I do something like redirect_to account_path(current_user.account) it uses the POST route instead of the GET one.
I'm decently new at Rails, so sorry for misunderstandings. Thanks for your help!

If Farm belongs_to Account (as opposed to HABTM), shallow nesting might be a good route structure.

I've figured out that I can do this by adding one more line in the routes file
get "/:account_slug/:id", to: "farms#show", as: 'account_farm'
Then I can link to a farm in the view with
<%= link_to farm.name, account_farm_path(account_slug: #account.slug, id: farm.slug)
I'm still interested in any answers about if this is good practice or not, or any other recommendations.

Related

How does Airbnb route each article?

I am making a website with Ruby on Rails, referencing Airbnb, and I found it difficult to make the following URL structure.
https://www.airbnb.com/help/article/767/what-is-the-resolution-center
In this URL, it seems that there is an article resource under help, so 767 is the ID of the article. In addition, after the ID, there is the name of the page in the URL.
scope '/help' do
resources :articles
end
By doing this, I was able to route until this part.
/help/articles/'page_id'
But I have no idea what to do from here.
I generated article model(title:string, content:text), and I am guessing that each article(title and content) is displayed according to the id.
Here's my questions.
How does Airbnb route page_name after the article ID?
In the case that the title and content in the article table are displayed, how do you put hyperlinks or internationalize the contents with I18n?
Also, please tell me if my guess is wrong in the first place, and tell me how Airbnb routes each article.
Thank you.
To tell Rails to accept request under URL like /help/article/767/what-is-the-resolution-center is actually very easy when you do use get instead of nested resources:
get '/help/article/:id/:slug', to: 'help_atricles#show', as: 'help_article'
Given the above URL, Rails would extract the id and slug from the URL and route the request to the show method of a HelpArticlesController.
That controller method could be as simple as:
def show
#article = HelpArticle.find(params[:id])
end
And to build such URLs you can use the help_article_path helper method defined by the as: 'help_article' part:
<%= link_to(
#article.title,
help_article_path(
id: #article.id,
slug: #article.title.parameterize
)
) %>
Read more about this routing method in the Rails Guides
Btw. I didn't use the slug part of the URL because it feels to me like it makes the URL look nicer and might be SEO relevant, but it feels useless from the application's point of view. You might want to use the slug part to identify the locale in which the article should be shown if you do not want to use the browser's locale setting.

link_to 'new' action in a different controller?

I want to have a link at the bottom of my show.html.erb that links to the new action in a different controller.
class Sample < ActiveRecord::Base
belongs_to :song
end
class Song < ActiveRecord::Base
has_many :samples
end
So, at the bottom of the show action for songs, I want to link to new for samples. This seems pretty easy, but I'm struggling to figure this out. I would also like to pass the id from the song to the form as :song_id
Fiddy, because you're new, let me explain how this works...
Routes
Your problem is that you don't understand the Rails routing structure - I'll hopefully explain it for you.
Rails, since it's an MVC framework, builds a series of "routes" for you. These "routes" are stored in the file available at config/routes.rb.
Routes, as described by the Rails documentation are as follows:
The Rails router recognizes URLs and dispatches them to a controller's
action. It can also generate paths and URLs, avoiding the need to
hardcode strings in your views.
The most important thing you should consider here is the way the routes generate paths for you. These paths are simply Rails "helper" methods, which you can call from your views. The reason these exist is two-fold -
They provide you with a DRY (don't repeat yourself) way of accessing / manipulating data
They are constructed around objects, helping maintain the object-orientated nature of Rails
These will likely mean nothing to you. However, what you need to realize that if set up your routes correctly, it seriously helps your app's infrastructure immensely.
--
Rails
This leads us quite nicely onto appreciating the way in which Rails works
Rails is an MVC (model view controller) framework. This might seem somewhat trivial, but in reality, it's one of the most important aspects to learn about Rails development, and here's why:
The Rails software system works by taking "requests" (user input) and then routing them to specific controller#actions. Those controllers then build model data from the database, and will translate that into either variables or objects, which you can use in your view.
The reason I mention this is that this type of development takes a lot of getting used-to, in that your program's flow is not about logic / functionality, but the accessibility of data. Therefore, when you ask about the routes or other parts of your app, you need to firstly remember what data you wish to show, and also how you want that data to be shown - this will give you the ability to construct & use the routes / controller actions which will get it to work properly
--
Fix
In terms of what you're saying, the way you'd go about achieving the result you want will be to use a nested route:
#config/routes.rb
resources :songs do
resources :samples #-> domain.com/songs/:song_id/samples/new
end
This will create a new route for you (which you can check by firing rake routes in your rails c (console). This will give you a path to use for your samples#new action:
#app/views/songs/show.html.erb
<%= link_to #song.name, new_song_sample_path(#song) %>
The above link will take you to the samples#show action, which you'll be able to populate with as much data as you require from the samples controller. The important thing to note is this action will have params[:song_id] available for you to either build an object from, or otherwise
<%= link_to "New Sample", new_sample_path(:song_id => #song_id) %>
Where #song_id is the variable that has that id in it.
Set paths in link_to tag which you can get by running rake_routes in terminal.
Ex
link_to "New song", new_sample_path(#song)
In the example given above #song is the instance variable of your current page.
You can also get some idea from here:
link_to Base URL Randomly Changed
Song Model:
accepts_nested_attributes_for :sample, allow_destroy: true
Route:
resources :songs do
resources :samples
end
Song's Show file:
<%= link_to "New Sample", new_song_sample_path(#song) %>
in url it will be:
/songs/:song_id/sample/new
Try this and let me know it works or not... I hope this helps you

Do I generate multiple controllers?

I'm on the rails learning journey and am going about making my first rails app. It's a very simple app where users can create posts on a variety of topics.
I generated my first scaffold for a page I want to have called 'London' (rails generate scaffold london location:string content:text). Users of the site can post a post and location of a place to visit in London.
Then I wanted to replicate this functionality for 'Paris'. Do I generate a new scaffold or go about it a different way? Some advice would be appreciated.
Also the url gets pluralized (mywebsite.com/londons). I added
resources :londons, :path => "london"
which changed the url but when I go to make a post I get a No route matches [POST] "/londons" error. Anyone got a fix for this?
Thank you!
well instead of generating controllers for each city a better way could be to create relationships between models.For example you could create a cities and a locations scaffold then inside your city model you can do
has_many :locations
and inside you locations model you can do
belongs_to :city
that way you wouldn't need to create new scaffolds for every city.You can read up on how to use relationships from the guides here
Well probably You want to generalise things first :)
What You actually need are pages (or maybe topics, articles). You can implement Page model that will have such attributes as title (which can be London, Paris etc).
The You will introduce a PagesController. index action will lead to a list of pages, show will render particular page.
In your routes You will do something like this:
resources :pages

Drupal-like routing system in Rails

I am trying to find a best-practice to allow users to define the route to their pages in Rails, by writing them in a text field when submitting posts, like with the Path module in Drupal (yes, we are porting a Drupal site to Rails)
So, I need to
define a new, named route on article submission (eg http://www.domain.com/a-day-in-annas-life)
change the existing route on article edit, if they define a new one, by doing a 301 redirect from the old route to the new one
How can I best achieve this?
Okay, I found a way, but if it's best practice or not, I cant say.
I am using custom restrictor's like this:
class CharitiesRestrictor
def self.matches?(request)
slug = request.path_parameters[:path]
!Charity.find_by_name(slug).nil?
end
end
constraints CharitiesRestrictor do
match '*path' => 'charities#show_by_slug', :constraints => CharitiesRestrictor.new
end
When I create a block like this for each of my model/controller pairs that should be able to respond to permalinks, I can have them all have a chance to act on a permalink. However, this also means that they all get called in series, which is not necessarily ideal.

URI management and navigation in Ruby on Rails

I'd like to implement a special routing in rails based on the URI (I'm using rails 3.0.4 with Mongoid and devise). Let's say my user logins, once signin I want to redirect him to a private area for example http://www.mysite.com/site1. Many users could belong to site1, many to site2... users of one site are not authorized to see another site. Today the redirection is fine after sign in, but I'm confused on how I should implement siteX (each site has its own data). I have the following route:
match '/:site_name' => 'site#index', :constraints => { :site_name => /'a pattern'/ }
resources :sites do
end
Because I need to stick to this URI format should I nest all my other resources inside :sites? For example if I want to display order 1 of site 2 the URL should look like http://www.mysite.com/site2/order/1. I can't put the resource's name "sites" in the URI as it starts directly with the identifier. Is there another way of doing this, what would be the best pratices? Hope my explanations make sense.
Thanks a lot for all your help!
Ted
I recommend you scrap the idea of "subdirectories". You'll have (not insurmountable) difficulties with link_to and the other helpers.
I would setup subdomains (a la, site1.mysite.com) if that's possible for your situation
Doing url.com/site_name is kind of nuts.
If only one user can belong to a site, take it from the user perspective then and use resource and not resources.
E.g., url.com/orders would be all current_user.orders, since current_user has_one site (or is a member of one site).
If you need site specific navigation, then draw from /site_name for site specific detail that is public in nature. E.g., url.com/site_name/pricing
If you really want to break your site down into /site_name specific routes, then through that into a subdomain. You can even try using sudomain devise to get you started.

Resources