How to update attributes of another model in Rails controller - ruby-on-rails

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 ?

Related

Redirecting to another view in rails

I'm trying to build a link shortener. The intended behavior is that on the first page (new) the user inserts his long link and presses a button, then he gets redirected to an another page called result, where a preset message will be waiting for him, along with both his short and long link.
I'm struggling with controllers, however, as no matter what I do something always comes wrong. Right now my controller looks like this:
class UrlsController < ApplicationController
def new
#short_url = Url.new
end
def create
#short_url = Url.new(url_params)
if #short_url.save
flash[:short_id] = #short_url.id
redirect_to "/urls/result"
else
render action: "new"
end
end
def show
Url.find(params[:id])
##short_url_yield =
redirect_to #short_url.url
end
def result
end
private
def url_params
params.require(:url).permit(:url)
end
end
And the routes.rb:
Rails.application.routes.draw do
resources :urls, :only => [:show, :new, :create, :result]
get 'urls/result' => 'urls#result'
root to: redirect('/urls/new')
end
When I submit the link, however, rails returns the following error:
Couldn't find Url with 'id'=result
Extracted source (around line #17):
def show
Url.find(params[:id])
##short_url_yield =
redirect_to #short_url.url
end
It seems I don't understand the logic behind it. What's going wrong? Isn't the show bit supposed to be a redirect that happens when I click the shortified link?
Rails routes have priority in the order they are defined. Since your SHOW route declaration is before get 'urls/result' => 'urls#result' the url gets matched as /urls/id=result.
Simply move your custom route above the resources block or use a collection block.
resources :urls, :only => [:show, :new, :create, :result] do
collection do
get 'result'
end
end
Using the collection and member blocks tells Rails to give priority to the routes inside over the normal CRUD actions such as show.

How do I create shipments at the time an order is created through the Spree API?

I have an application that integrates with the Spree API. In the default flow of the Spree API, the shipments are created when an order transitions to delivery as per line 90 from core.app.models.spree.order.checkout.rb:
before_transition :to => :delivery, :do => :create_proposed_shipments
Because I am specifying both the line items and the shipping address during my initial post to create the order, I want Spree to create the shipments too.
So far I created an orders_controller_decorator where I overrode the create method with the original create code plus a call to create_proposed_shipments, but I am getting NameError (uninitialized constant Order) from the code I copied from the original create method.
My current orders_controller_decorator:
Spree::Api::OrdersController.class_eval do
def create
authorize! :create, Order
#order = Spree::Core::Importer::Order.import(current_api_user, order_params)
#order.create_proposed_shipments
respond_with(#order, default_template: :show, status: 201)
end
end
I had the right idea. Once I fixed the NameError related to Order by specifying the module, everything worked as expected.
module Spree
Api::OrdersController.class_eval do
def create
authorize! :create, Order
#order = Core::Importer::Order.import(current_api_user, order_params)
#order.create_proposed_shipments
respond_with(#order, default_template: :show, status: 201)
end
end
end
alternatively, I could have done
Spree::Api::OrdersController.class_eval do
def create
authorize! :create, Spree::Order
#order = Core::Importer::Order.import(current_api_user, order_params)
#order.create_proposed_shipments
respond_with(#order, default_template: :show, status: 201)
end
end

adding shallow nested associations controller before action

I'm use a short form of shallow routing:
resources :officers, except: :new
resources :members do
resources :officers, only: :new
end
This is a member has_many officers : officers belong_to member association. Members can fill many officer roles over the years, the reason for has_many.
I've had problems dealing with this for years and decided to ask a question on the preferred Rails way of handling the creation of an associated has_many record.
On the Members show page I have a link:
= link_to 'New Officer', new_member_officer_path(#member)
There is no new_officer_path link in the application, but I wanted to trap someone putting in officers/new in the url. In the Officers _form partial, I set a hidden field member_id to #member.id if its a new record (id.nil?)
My first failed attempt:
def new
set_member
#officer = Officer.new(member_id: #member.id)
end
def set_member
if params[:member_id]
#member = Active.find(params[:member_id])
else
redirect_to members_path, alert: "Officers must be created from the Members Show view"
end
end
This failed, I guess because you can't redirect from a called method from an action.
I then tried:
before_action :set_member, only: :new
def new
#officer = Officer.new(member_id: #member.id)
end
def set_member
if params[:member_id]
#member = Active.find(params[:member_id])
else
redirect_to members_path, alert: "Officers must be created from the Members Show view"
end
end
This worked in a valid members/xx/officers/new url call but Failed in an on officers/new url. Since there is not a new route, it went to the show action and failed try to find :id new
Removing the only: :new condition on resources officers allow that approach to work, but I have an unneeded route.
Moving stuff around in my first attempt:
def new
set_member
if #member
#officer = Officer.new(member_id: #member.id)
else
redirect_to members_path, alert: "Officers must be created from the Members Show view"
end
end
def set_member
if params[:member_id]
#member = Active.find(params[:member_id])
end
end
Works, but for some reason does not feel right. I almost like leaving the new route in and the before_action approach.
Again, just looking for the standard approach. Also, is setting the foreign id in a hidden field the standard approach?
Personally I would write it like this:
def new
#member = Active.find(params[:member_id]) if params[:member_id].present?
if #member.present?
#officer = #member.officers.build
else
redirect_to members_path, alert: "Officers must be created from the Members Show view"
end
end
Using present? avoids any potential issues with the way Ruby handles nil as false-y. Not a huge concern, but I think it's best practice to return booleans instead of memorizing Ruby's interpretation of objects to boolean.
Using build automatically sets the member_id. And I didn't feel that set_member needed to be it's own method. However, if the logic in that method gets more complicated, I might revert back to using set_member.

Rails - nesting routes within the same controller

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

Creating custom routes where a model attribute is used instead of the ID in Rails

I have a model called Student, which has an attribute called University_ID in it.
I created a custom action and route which displays the details of a specific student via the following link :students/2/detailsi.e. students/:id/details
However, I want to be able to allow the user to use their university ID instead of the database ID so that the following would work for instance students/X1234521/details
i.e. students/:university_id/details
My route file looks like this at the moment:
resources :students
match 'students/:id/details' => 'students#details', :as => 'details'
However this uses the Student_ID as opposed to the University_ID, and I've tried doing
match 'students/:university_id/details' => 'students#details', :as => 'details', but that only corresponds to the Student_ID, not the University_ID.
My controller looks like this, if this helps in any way:
def details
#student = Student.find(params[:id])
end
I also tried doing #student = Student.find(params[:university_id]) but nope, nothing worked.
After chatting with #teenOmar to clarify the requirements, here's the solution we came up with, which allows for the existing students/:id/details route to accept either an id or a university_id (which starts with a w), and uses a before_filter to populate #student for use in various controller actions:
class StudentsController < ApplicationController
before_filter :find_student, only: [:show, :edit, :update, :details]
def show
# #student will already be loaded here
# do whatever
end
# same for edit, update, details
private
def find_student
if params[:id] =~ /^w/
#student = Student.find_by_university_id(params[:id])
else
#student = Student.find(params[:id])
end
end
end

Resources