Do I generate multiple controllers? - ruby-on-rails

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

Related

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

How to create a scaffold with differing controller and model names?

I'm making a versioned JSON API in rails, where the controllers also respond to HTML, meaning it can be accessed as a browser or through an app I'm developing. The controllers have the form Model::V1::UsersController (Model instead of API since they don't just respond to JSON), and I currently have the following in my routes.rb:
namespace :model, path: 'm', as: '' do
# For objects in the model, accessible by JSON (through the app) or HTML (through the browser, using forms to send data to the server).
scope module: 'v1', constraints: OrConstraint.new([APIConstraint.new(1), APIConstraint.new(:default)]) do
resources :users do
collection do
post :sign_in
end
end
end
end
I plan to add more models to my API, but how can I use scaffolding to do this? For example, to create a controller Model::V1::CommentsController, but using the Comment model, instead of Model::V1::Comments.
I've been trying to figure this out for hours, and googling for people with similar problems shows that a few people say not to use scaffolding at all in this case: I don't want to do this, as it would mean writing all the views myself, which would be very time-consuming. Apart from that, I can't find much. nifty-generators was suggested somewhere, but it doesn't seem to be maintained anymore: no activity since 2012. I'm new to rails, and it might be that I've missed something quite obvious, but I find it surprising that not many others have had the same issue.
I've considered making my own generator, but looking at the source of https://github.com/rails/rails/blob/master/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb, it seems very complicated.
EDIT: I've just discovered that I can pass the --model-name parameter to the rails scaffold generator to achieve what I want, but for some reason it still tries to create a model with the same name as the controller. How can I change this?
I've settled with this solution, by not generating a model at all using the scaffold generator:
To create Model::V1::CommentsController as the controller and Comments as the model:
rails g model comment
rails g scaffold model/v1/comments --model-name=comment --no-orm

Ruby on Rails Routing, Double Nested Slugs

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.

Rails nested resource creation on separate pages

resources :books do
resources :chapters
end
Let's assume I have the above properly nested resources. I want to create a page where I create parent book resources and another page to create the chapters resources. When creating chapters, I want users to be able to select parent books they created.
Right now I have...
protected
def find_book
#book = Book.find(params[:book_id])
end
...in the chapter controller but I believe this only works when there is already a book id present in the URL. So to create a new chapter I would have to visit "rootpath/book/book_id/chapter/new" when I want to be able to create chapters on a separate page.
Although I'm really not sure how to approach the problem, right now my plan is to put an association(?) form on the chapter creation page that links the nested resources.
The problem is, I'm really new to web development and I'm not sure if I'm approaching this right at all. How would I put a form that sends :book_id to the chapter controller? Would this method work at all? Are there more efficient ways to go at it?
I realize my questions might be a little vague but Any help would be greatly appreciated!
The dull answer is: your proposal does not make sense with only the nested route.
The nested route implies that upon accessing the chapters#new action, you already know exactly which book that should contain the chapter.
But on the bright side: you can use both nested and non-nested routes at the same time.
If you want to keep the nested route, but also provide a new and create actions that lets the user choose the desired Book for the chapter, you can add a non-nested route for Chapter creation.
For example:
resources :books do
resources :chapters
end
resources :chapters
Note that your controllers may need to be rewritten a bit to accomodate the dual routes.
If you want, you could create both resources in the same page. Look up accepts_nested_attributes_for to do that. It's really easy, once you get the hang of it.

Why does Rails use plurals for new and create?

I understand why a Rails index method would use the plural form of a resource - we're showing all projects, for example.
And I understand why the show method would use the singular form - we only want to see one project, with a particular ID.
But I don't understand why new and create would use the plural. Is there a way to create more than one project at a time? Is there some other reasoning for using the plural here that someone could explain?
New and Create aren't plural, in the way I think about REST. Instead, I think about it like:
whatever.com is your base domain, and whatever.com/books means that you have a collection of resources each named book. The collection itself is named books.
So, when you want to create a new book, you are asking the collection for the information needed to create a new book. This becomes /books/new
When you actually create the book, you are posting information to /books. The HTTP verb is POST, so when you POST to your collection, you execute the create action.
This looks like a good starting point on REST.
I thought they were always plural. Scroll down a bit on this page for an example of the routes generated by resources :photos
Whether you're GETting a single resource or POSTing to the collection, you're still in the domain of photos. So, search the domain of photos given an id, POST a new photo to the domain of photos, etc.

Resources