Rails - nesting routes within the same controller - ruby-on-rails

I'm totally confused trying to route my app. I have a bunch of events, and each should have a photo gallery.
However I would like to keep everything within the same EVENT controller (btw, another question - how reasonable is that?). So the user can go on the Edit Event page and have a menu on the left side with links, one of which will be his gallery.
So I've added this to my event controller:
def gallery
#event = Event.find(params[:id])
end
The URI should be (I guess?): site/event/777/gallery/edit
How can I route that? And what will be the _path?
Thank you for any help

I can think of no good reason to do this. Creating another file is trivial, have a GalleriesController with the usual show/edit/update/etc methods.
In your routes:
resources :events do
resources :galleries
galleries_controller.rb:
class GalleriesController < ApplicationController
# GET /events/1/galleries/1/edit
def edit
#event = Event.find(params[:event_id])
#gallery = #event.galleries.find(params[:id])
end
end

The following configuration in your config/routes.rb should give you what you want:
resources :events do
resources :galleries
end
And this will give you event_galleries_path. And, this will give you event_galleries_path. Following are the seven paths the above configuration will provide:
event_galleries GET /events/:event_id/galleries(.:format) galleries#index
POST /events/:event_id/galleries(.:format) galleries#create
new_event_gallery GET /events/:event_id/galleries/new(.:format) galleries#new
edit_event_gallery GET /events/:event_id/galleries/:id/edit(.:format) galleries#edit
event_gallery GET /events/:event_id/galleries/:id(.:format) galleries#show
PUT /events/:event_id/galleries/:id(.:format) galleries#update
DELETE /events/:event_id/galleries/:id(.:format) galleries#destroy
The edit named route is: edit_event_gallery_path.
Then instead of adding the gallery method in your EventsController, so you'd create your edit, show and other other actions in your GalleriesController.
# /events/:event_id/galleries/:id/edit
def edit
#gallery = Gallery.find(params[:id])
end
# /events/:event_id/galleries/:id
def show
#event = Event.find(params[:event_id])
# And your galleries, something like this
#galleries = #event.galleries.find(params[:id])
end

Related

How to update attributes of another model in Rails controller

I am a part of a team that is creating a chess app in Rails 5. I have a controller named "chess_pieces.rb". In there I have the following code:
def update
#piece = ChessPiece.find(params[:id])
#game = Game.find_by_id(#piece.game_id)
#move = #piece.moves.all
#piece.update_attributes(pieces_params)
if #piece.valid?
redirect_to game_path(#game)
else
render :edit, status: :unprocessable_entity
end
update_moves
end
def update_moves
puts "The piece has been updated!"
#piece = ChessPiece.find(params[:id])
#piece.moves.update(count: 19991)
#piece.save
puts #piece.moves.count
end
As you can see the update method, is updating a piece when it is moved, specifically the x_position and y_position on the chess board. Now, how do I make the method update_moves update the attributes of a table called moves which is associated with the chess_piece.rb model. I am calling this method in the update method after a piece´s location on the board is updated.
I want to update the count of how many moves the piece has made + some other stuff.
My routes are the following:
Rails.application.routes.draw do
devise_for :users
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
root 'games#home'
resources :games do
resources :pieces, only: [:show, :update]
patch 'update_moves', on: :member
end
end
Is the relationis is (piece has_many moves) ?
If so, I don't think you can update all moves of a piece with #piece.moves.update(count: 19991) . You should update them individually with an iteration or a map.
Note: If you are trying to add a new move record to the piece then you should use #piece.moves.build(count: 19991)
If the relation is one to one then you should try replacing #piece.moves.update(count: 19991) with #piece.moves.count = 19991 ?

Rails post model for news and blog view

I have a post model and posts controller with an attribute format so id like to split the views out based on this attribute i.e. foo.com/blog/xxx if the format is blog and foo.com/news/xxx if the format is news.
resources :posts
def index
#posts = Post.where(draft: 'false').order(publish_date: :desc)
end
def show
#post = Post.find_by!(slug: params[:id])
end
I can easily so something like this for the index view but im unsure about the show route
get '/new', to: 'posts#news'
def news
#posts = Post.where(draft: 'false', format: 'news').order(publish_date: :desc)
end
You can try this way:
Create two different methods to use within your routes.rb, they will be inside the only one controller you want to have "PostsController", and make a verification of the params attribute in every #post that pass through the show method, something like:
class PostsController < ApplicationController
def show
#post = Post.find(params[:id])
if #post.format == 'news'
redirect_to news_index_path
elsif #post.format == 'blog'
redirect_to blog_index_path
end
end
def blog_index
end
def news_index
end
...
Then specify in your routes.rb the path to every method, as you made within the show method in order to use the redirect_to method:
get '/blog/:id', to: 'posts#blog_index', as: 'blog_index'
get '/news/:id', to: 'posts#news_index', as: 'news_index'
This is, when you pass a #post with the format attribute equal to blog you'll be redirected to the blog_index_path, which will load the blog_index view within your posts folder in app/views/posts. Something seemed will happen when the #post.format is news.
├── blog_index.html.erb
├── index.html.erb
├── news_index.html.erb
└── show.html.erb
0 directories, 4 files
This will have a very little delay and a very quick change in the route, everytime you have to pass from posts#show to the next route.
I've made a repo, in order you can check how this works.
To indicate a show route, you need to pass id as a parameter in your route.
get '/blog/:id' => 'posts#blogs'
I guess this is what you are looking for.

(Rails) How to get 'id' out of edit url

I have a model called studies.
After action redirect redirect_to edit_study_path(#new_study),
URL: http://localhost:3000/studies/2/edit.
Is there anyway to customize an url after passing id ?
For example, http://localhost:3000/study
(still going to the edit path, and still with the :id in the params)
I guess what you want is to edit the current study?
In this case, it's possible, using ressource instead of ressources in the routes.
Let's have an example:
#in routes.rb
resources :studies
resource :study
Both of them will by default link to the StudiesController and call the same actions (eg. edit in your case) but in two different routes
get "/studies/:id/edit" => "studies#edit"
get "/study/edit" => "studies#edit"
in your edit action, you should then setup to handle correctly the parameters:
def edit
#study = params[:id].nil? ? current_study : Study.find(params[:id])
end
Note you need a current_study method somewhere, and store the current_study in cookies/sessions to make it works.
Example:
# In application_controller.rb
def current_study
#current_study ||= Study.find_by(id: session[:current_study_id]) #using find_by doesn't raise exception if doesn't exists
end
def current_study= x
#current_study = x
session[:current_study_id] = x.id
end
#... And back to study controller
def create
#...
#Eg. setup current_study and go to edit after creation
if study.save
self.current_study = study
redirect_to study_edit_path #easy peesy
end
end
Happy coding,
Yacine.

Rails: Forum has_many topics, saving the association?

I might be going about this the wrong way in the first place, so I will give a bit of background first.
As you can tell from the title, I am building a forum from scratch. I thought it was working correctly; however, I am a bit unsure as to how to update/save the forum object from within the topics "create" method in it's controller.
What I tried to do:
In the "New" method, I sent the Forum's id via the routing. So on the new-topic page has a address that looks like this: "localhost:3000/new-topic/1". The one being the Forum's id. In the method itself, I try to attach it to the new topic object.
#topic = Topic.new
#topic.forum = Forum.find(params[:id])
My create method then tries to use that forum.
#topic = Topic.new(params[:topic])
#topic.forum.topics << #topic #Simplified down.
if #topic.save
#topic.forum.save
...
I get the feeling that I am going about this the wrong way. I was looking at someone's tutorial and they got the forum by calling params[:forum_id] but they didn't show they routing they did to achieve that.
How do I do this correctly and/or what is the correct way to route all of this? For the record, I do plan on using this same method for the Topic => Post association. Thanks for any help.
You should use nested REST routes:
# routes.rb
resources :forums do
resources :topics
end
this will result in the following routes:
GET /forums/:forum_id/topics/new # displays the form
POST /forums/:forum_id/topics # creates the topic
and in controller you should use builders, they have several advantages like security, scope preserving etc.:
def new
#forum = Forum.find(params[:forum_id])
#topic = #forum.topics.build
def create
#forum = Forum.find(params[:forum_id])
#topic = #forum.topics.build(params[:topic])
if #topic.save
...
http://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Resources.html#method-i-resources

Best Practice for views to nested resources in Rails?

I have a fairly simple model; Users have_many products. I would like to be able to view a list of all products as well as a list of the products associated with a given user. My routes are set up like this:
/products
/products/:id
/users
/users/:id
/users/:id/products
The catch here is that I'd like to display the product list differently in the product#index view and the user/products#index view.
Is there a 'correct' way to do this? My current solution is to define products as a nested resource inside users, and then to check for params[:user_id] - if its found I render a template called 'index_from_user', otherwise I just render the typical 'index' template.
This is a situation I'm running into a lot - if there's a preferred way to do it I'd love to know...
You can declare two "products" routes - one under users, and one independent of users eg:
map.resources :products
map.resources :users, :has_many => :products
They will both look for "ProductsController#index" but the second will have the "user_id" pre-populated from the route (note: "user_id" not just "id")
So you can test for that in the index method, and display different items depending on whether it is present.
You will need to add a before_filter to the ProductController to actually instantiate the user model before you can use it eg:
before_filter :get_user # put any exceptions here
def index
#products = #user.present? ? #user.products : Product.all
end
# all the other actions here...
# somewhere near the bottom...
private
def get_user
#user = User.find(params[:user_id])
end
If you really want to display completely different views, you can just do it explicitly in the index action eg:
def index
#products = #user.present? ? #user.products : Product.all
if #user.present?
return render(:action => :user_view) # or whatever...
end
# will render the default template...
end

Resources