Today I tried to use a wonderful callback :after_commit which triggers when the object is written to database, however, I've got the error message from Rails:
ActionController::RoutingError (undefined method `after_commit' for ImagesController:Class
Did you mean? after_action):
Well, that was embarassing! And it seems like this callback was deprecated!
Looking through search, I tried to use :after_create_commit, which gave me the same error.
The third step was to try :after_action. Here goes the question:
How to make it work the same way as :after_commit?
I've already tried apidock.com - it's really minimal! Also I've tried api.rubyonrails.org - it is saying about blocks, but I'm not a ruby ninja to understand it. So I really apprecite if you could spill some light on it!
ImagesController:
class ImagesController < ApplicationController
after_create_commit :take_image_to_album
def take_image_to_album
if check_album
add_inner_to_album(#image)
end
end
def create
#image = Image.create(image_params.merge(:user_id => current_user.id)
respond_to do |format|
unless #image.save
format.html { render :show, notice: "Error!" }
format.json { render json: #image.errors, status: :unprocessable_entity }
else
format.html
format.json { render :show, status: :ok, location: #image }
end
end
end
...
def add_inner_to_album(image)
contents = #album.content
contents << [image.id, image[:imageup], false]
#album.update(:content => contents)
end
end
The after_commit method is only for models. In the controllers family, you have after_action, that will be executed after the action of the controller is finished.
For example, the after_action in a controller works like this:
class UsersController < ApplicationController
after_action :log_activity, only: :show
# GET v3/users/:id
def show
render status: :ok,
json: { id: current_user.id, name: current_user.name }
end
def log_activity
current_user.update(last_activity_at: Time.current)
end
end
The log_activity method is executed after responding the request.
In the after_action :log_activity, only: :show, with only you can specify after which actions log_activity will run. If you do not specify any, it will run after every action defined in the controller.
Related
How do I go about making this code block dry?
I understand that dry means dont repeat yourself but I don't see any obvious opportunity for refactoring.
Index, show, edit, and create seem like basic/necessary methods. They appear to be pretty dry to me.
The methods after I am not sure about.
I haven't tried anything other than googling so far.
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def edit
#user = User.find(params[:id])
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
Slack.notify_channel
end
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
Slack.notify_channel
end
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
Slack.notify_channel
end
private
def user_params
params.require(:user).permit(:username, :email)
end
end
There is no rails backend attached to this code snippet. I am assuming it is just theoretical - wanting us to refactor the code to make it shorter.
IMHO you can do something like that.
class UsersController < ApplicationController
include ExceptionHandling
before_action :load_user, only: [:show, :edit, :update, :destroy]
after_action :slack_notify_channel, only: [:create, :update, :destroy]
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.create!(user_params)
respond_to do |format|
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
end
end
def update
#user.update!(user_params)
respond_to do |format|
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
end
end
def destroy
#user.destroy!
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def load_user
#user = User.find(params[:id])
end
def slack_notify_channel
Slack.notify_channel
end
def user_params
params.require(:user).permit(:username, :email)
end
end
I would recommend you to create a concern to manage exceptions and render each specific error by exception. Then you can avoid having two ways in each action to render a good and bad case.
I have to guess a bit about what's in your ApplicationController and User classes. But the obviously repeated code is #user = User.find(params[:id]). Your show and edit methods both just run this line. So, they do exactly the same thing, which they oughtn't to do. One method for one thing.
Also, once you resolve show and edit into a single method, your create, update and destroy methods should call it instead of repeating the line.
Next, I wouldn't use new as a method name, since it is already used by BasicObject::new. I tested a few things and it gets pretty unclear:
class Test
attr_reader :test
def initialize
#test = 'test'
end
end
class Test2
attr_reader :test2
def new
p 'test2new'
#test2 = Test.new
end
end
testx = Test2.new
p testx.new.test
p testx.test2.test
=> "test2new"
=> "test"
=> "test"
It takes extra effort to see when you're calling your own new method and when you're calling BasicObject::new. So, I'd change that to new_user or something, if you even need it at all — I don't see why you need both it and your create method. (If you don't need it, get rid of it.)
Finally, #user = User.find(params[:id]) doesn't imply either showing or editing, so understanding what you're trying to do and coming up with a name that reflects it (e.g. set_user_id) is something that ought to be done as well.
I have following table in database and also the model created for it.
|id |name |description |created_date |updated_date |
-----------------------------------------------------------------------
|1 |HELLO |greeting |2017-09-28 18:51:51 |2017-09-28 18:51:51|
model.rb
class Person < ApplicationRecord
has_many :person_activities
validates :name, uniqueness: true
end
I want to create update action in controller. It will update the name and description based on the name passed. e.g i want to update the name HELLO to HI. How can i create update action for that?
I tried following update action in controller but it wont hit the update action
def update
byebug
redirect_to Person.find(name: params[:id]).tap { |person|
person.update!(person_params)
}
end
private
def person_params
params.require(:person).permit(:name)
end
routes.rb
resources :person, only: [:index, :show, :create, :update, :destroy], defaults: { format: :json }
Tried testing like this http://localhost:1111/person/HELLO and passing { name: 'HI'} in body params. I am using postman for testing.
You can modify your model, something like this:
class Person< ActiveRecord::Base
before_save :change_name
private
def change_name
self.name="HI" if self.name=="HELLO"
end
end
To answer this properly we would need to understand the logic of how creating a new record with name = "HELLO" would need to be set to "HI". Do you have a lookup table for this logic or is it hardcoded?
The easiest thing I could think of is you can change the value of the passed parameter before doing the update by setting the value of the parameter. E.G.
params["name"] = "HI"
So when you do a person.update it will use the "name" param set to "HI".
Then you could do this in a
def update
respond_to do |format|
params["name"] = "HI"
if #person.update(person_params)
format.html { redirect_to #person, notice: 'Person was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #person.errors, status: :unprocessable_entity }
end
end
end
You could also do the update then just set the value after the update:
def update
respond_to do |format|
if #person.update(person_params)
#person.name = "HI"
#person.save
format.html { redirect_to #person, notice: 'Person was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #person.errors, status: :unprocessable_entity }
end
end
end
From postman you need to call login action first. So it will create a session in postman and then you can call update action.
Im building out an app in Rails 4 Ruby 2
Background:
I have a parent scaffold named "Calls" I then have a child scaffold named "Respondings". What I am attempting to achieve here is to have the ability to update items in the "Respondings" table from the "Calls" show.html.erb by way of a custom button click. (Shown below)
My routes look like so:
resources :calls do
resources :respondings, except: [:index], controller: 'calls/respondings' do
member do
patch :unit_responding_update
end
end
resources :pings, except: [:index], controller: 'calls/pings'
resources :agencies, except: [:index], controller: 'calls/agencies'
resources :incidents, except: [:index], controller: 'calls/incidents'
resources :complainants, except: [:index], controller: 'calls/complainants'
end
end
The patch: unit_respondings_update is what custom method that will update the table when pressed.
The Custom Def Rake output Here:
unit_responding_update_call_responding PATCH /calls/:call_id/respondings/:id/unit_responding_update(.:format) calls/respondings#unit_responding_update
My problem is that i have tried to place this in both the calls_controller.rb and the calls/respondings_controller.rb both to no avail. Listed below are the two controllers in their entirety. the other BIG Problem i am having is generating the path in the button to have this read and perform the function.
Parent Controller "Calls_controller"
class CallsController < ApplicationController
before_action :set_call, only: [:show, :edit, :update, :destroy]
# GET /calls
# GET /calls.json
def index
#calls = Call.all
#active_calls = #calls.select{ |x| x.status == 'ACTIVE' }
#pending_calls = #calls.select{ |x| x.status == 'PENDING'}
#clear_calls = #calls.select{ |x| x.status == 'CLEAR'}
end
# GET /calls/1
# GET /calls/1.json
def show
#call = Call.find(params[:id])
## Responding Nested
#respondings = #call.respondings
## Ping Nested
#pings = #call.pings
## Agency Nested
#agencies = #call.agencies
## Incidents Nested
#incidents = #call.incidents
## Complainants Nested
#complainants = #call.complainants
end
# GET /calls/new
def new
#call = Call.new
end
# GET /calls/1/edit
def edit
end
# POST /calls
# POST /calls.json
def create
#call = Call.new(call_params)
#call.status = "PENDING"
respond_to do |format|
if #call.save
format.html { redirect_to #call, notice: 'Call was successfully created.' }
format.json { render :show, status: :created, location: #call }
else
format.html { render :new }
format.json { render json: #call.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /calls/1
# PATCH/PUT /calls/1.json
def update
respond_to do |format|
if #call.update(call_params)
format.html { redirect_to #call, notice: 'Call was successfully updated.' }
format.json { render :show, status: :ok, location: #call }
else
format.html { render :edit }
format.json { render json: #call.errors, status: :unprocessable_entity }
end
end
end
# DELETE /calls/1
# DELETE /calls/1.json
def destroy
#call.destroy
respond_to do |format|
format.html { redirect_to calls_url, notice: 'Call was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_call
#call = Call.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def call_params
params.require(:call).permit(:site_id, :call_number, :call_type, :call_type_other, :call_details, :user_id, :status)
end
end
Child Controller "calls/respondings_controller": Currently Where the Custom Method is found:
class Calls::RespondingsController < ApplicationController
#before_action :set_responding, only: [:show, :edit, :update, :destroy]
# GET /respondings
# GET /respondings.json
def index
#respondings = Responding.all
end
# GET /respondings/1
# GET /respondings/1.json
def show
end
# GET /respondings/new
def new
#call = Call.find(params[:call_id])
#responding = Responding.new
end
# GET /respondings/1/edit
def edit
end
# POST /respondings
# POST /respondings.json
def create
#call = Call.find(params[:call_id])
#responding = Responding.new(responding_params)
#responding.call = #call
respond_to do |format|
if #responding.save
format.html { redirect_to #call, notice: 'Responding was successfully created.' }
format.json { render :show, status: :created, location: #call }
else
format.html { render :new }
format.json { render json: #call.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /respondings/1
# PATCH/PUT /respondings/1.json
def update
respond_to do |format|
if #responding.update(responding_params)
format.html { redirect_to #responding, notice: 'Responding was successfully updated.' }
format.json { render :show, status: :ok, location: #responding }
else
format.html { render :edit }
format.json { render json: #responding.errors, status: :unprocessable_entity }
end
end
end
# DELETE /respondings/1
# DELETE /respondings/1.json
def destroy
#call = Call.find(params[:call_id])
#responding = Responding.find(params[:id])
if #responding.destroy
flash[:notice] = "Successfully removed unit from call."
redirect_to #call
else
flash[:error] = "There was an error removing this responder from this call."
end
end
#Custom Controller Calls
def unit_responding_update
#call = Call.find(params[:call_id])
#responding = Responding.find(params[:id])
#responding.responding_tme = DateTime.now
#responding.responding = "true"
#responding.on_scene = "false"
#responding.clear = "false"
#responding.status = "RESPONDING"
#responding.save!
respond_to do |format|
if #responding.save
format.html { redirect_to #call, notice: "Responding time successfully updated. Your status - RESPONDING" }
else
format.html { render action: 'edit' }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_responding
#responding = Responding.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def responding_params
params.require(:responding).permit(:call_id, :user_id, :responding_tme, :on_scene_tme, :clear_tme, :responding, :on_scene, :clear, :status)
end
end
If you need more information please don't hesitate to ask! i am at a standstill until this is resolved! ( or for access to my Git where this is stored for full context)
Thanks in advance to everyone!
EDIT # 1: -- Adds Calls / Respondings Model Relations
Model Relations For Call
class Call < ActiveRecord::Base
has_many :respondings, dependent: :destroy
end
Model Relations for Responding
class Responding < ActiveRecord::Base
has_many :incidents
belongs_to :call
end
So the Fix to this problem was soley in the routes file.. it turns out I was missing and end.. After changing my routes file to the following, it solved the initial problem altogether.
resources :calls do
resources :respondings, except: [:index], controller: 'calls/respondings' do
member do
patch :unit_responding_update
end
end
resources :pings, except: [:index], controller: 'calls/pings'
resources :agencies, except: [:index], controller: 'calls/agencies'
resources :incidents, except: [:index], controller: 'calls/incidents'
resources :complainants, except: [:index], controller: 'calls/complainants'
end
end
Once the routes were fixed I used the following link:
<%= link_to "Responding", unit_responding_update_call_responding_path(call_id: #call.id, id: #respondings.first.id), method: :patch %>
Before I begin, let me say that I have two different devise user models.
I began removing the unnecessary methods from the scaffold_controller, because devise handles certain actions.
I'm trying to route to the current_user profile page, but I get an error every time I try to add the link in my navbar <%= 'My Profile', current_user %> doesn't work in this case. I plan to setup the associations as soon as I finish hooking up these controllers.
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
# GET /profiles
# GET /profiles.json
def index
#profiles = Profile.all
end
# GET /profiles/1
# GET /profiles/1.json
def show
#user = User.find(params[:id])
respond_to do |format|
format.html
format.json {render :json => #user }
end
end
# GET /profiles/1/edit
def edit
end
# PATCH/PUT /profiles/1
# PATCH/PUT /profiles/1.json
def update
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to #profile, notice: 'Profile was successfully updated.' }
format.json { render :show, status: :ok, location: #profile }
else
format.html { render :edit }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# DELETE /profiles/1
# DELETE /profiles/1.json
def destroy
#profile.destroy
respond_to do |format|
format.html { redirect_to profiles_url, notice: 'Profile was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_profile
#profile = Profile.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def profile_params
params[:profile]
end
end
profiles GET /profiles(.:format) profiles#index
POST /profiles(.:format) profiles#create
new_profile GET /profiles/new(.:format) profiles#new
edit_profile GET /profiles/:id/edit(.:format) profiles#edit
profile GET /profiles/:id(.:format) profiles#show
PATCH /profiles/:id(.:format) profiles#update
PUT /profiles/:id(.:format) profiles#update
DELETE /profiles/:id(.:format) profiles#destroy
Assuming (such as bad thing to do), that you have already created the profile while creating the user ( since there is no create action in the controller ) and that a profile belongs_to a user, then this should work:
<%= link_to "My Profile", current_user.profile %>
Generally, I would get my associations set up properly before I even look at the controllers.
I have some code for a Rails 4 project I'm working on. It uses active_record (mysql2), and there is a has_many :through relationship that works properly when I interact through rails c (in either production or development). When I try to submit the relationship in a form (I am using simple_form), I can't seem to get it to save.
Here is how my information is currently set up (just showing snippets, I can't really show the whole source):
Model:
has_many :categorizations
has_many :resource_categories, through: :categorizations
accepts_nested_attributes_for :resource_categories
accepts_nested_attributes_for :categorizations
Form:
= simple_form_for #resource do |f|
= f.association :resource_categories
Controller:
# POST /resources
# POST /resources.json
def create
#resource = Resource.new(resource_params)
set_categories(#resource, params[:resource][:resource_category_ids])
respond_to do |format|
if #resource.save
format.html {
redirect_to #resource, notice: 'Resource was successfully created.'
}
format.json {
render action: 'show', status: :created, location: #resource
}
else
format.html {
render action: 'new'
}
format.json {
render json: #resource.errors, status: :unprocessable_entity
}
end
end
end
# PATCH/PUT /resources/1
# PATCH/PUT /resources/1.json
def update
respond_to do |format|
if #resource.update(resource_params)
set_categories(#resource, params[:resource][:resource_category_ids])
format.html {
redirect_to #resource, notice: 'Resource was successfully updated.'
}
format.json {
head :no_content
}
else
format.html {
render action: 'edit'
}
format.json {
render json: #resource.errors, status: :unprocessable_entity
}
end
end
end
# Never trust parameters from the scary internet, only allow the white list
# through.
def resource_params
params.require(:resource).permit(
:title, :slug, :ancestry, :status, :author_id, :published, :parent_id,
:resource_category_ids, :preview, :body
)
end
def set_categories(resource, categories)
# Clean out the existing categories (if there are any)
unless resource.resource_categories.blank?
resource.resource_categories.each do |category|
resource.resource_categories.delete(category)
end
end
unless categories.blank?
categories.each do |category|
unless category.blank?
resource.resource_categories << ResourceCategory.find(category)
end
end
end
end
When I issue the following commands using rails c -e production (or just plain rails c) it works (In this example, I assign all categories to all resources):
Resource.all.each do |resource|
ResourceCategory.all.each do |category|
resource.resource_categories << category
end
end
It seems like my problem is that the controller is not calling the helper function
Use this instead:
def create
#resource = Resource.new(resource_params)
#resource.set_categories(params[:resource][:resource_category_ids])
..
end
Move the method in the Resource model:
def set_categories(categories)
# Clean out the existing categories (if there are any)
unless new_record?
unless resource_categories.blank?
resource_categories.each do |category|
resource_categories.delete(category)
end
end
end
unless categories.blank?
categories.each do |category|
unless category.blank?
resource_categories << ResourceCategory.find(category)
end
end
end
end
#resource is instance variable of your Controller, you don't need to pass it to a method. Perform all your operations directly on the instance variable.
OP still had problem while saving the record, changed :resource_category_ids to :resource_category_ids => [] in resource_params method:
def resource_params
params.require(:resource).permit(
:title, :slug, :ancestry, :status, :author_id, :published, :parent_id,
:preview, :body, :resource_category_ids => []
)
end