Can't purge active storage attachments - ruby-on-rails

I have a model called Resource configured with:
class Resource < ActiveRecord::Base
has_many_attached :assets
end
I created an action in my resources_controller.rb as follows:
def delete_asset_attachment
#asset = ActiveStorage::Attachment.find_by(params[:id])
logger.debug "The value of #asset is #{#asset}"
#asset.purge
redirect_to #resource
end
I have a form that shows the resource and loops through the attached assets. Below is the snippet of code doing the loop through the assets:
<% #resource.assets.each do |asset| %>
<%= link_to 'Remove Attachment', delete_asset_attachment_resource_url(#resource, asset.id), method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>
The /resources page properly shows the resource along with the attached assets. However, when I try to click the link to delete one of the assets, I receive an error: "undefined method `purge' for nil:NilClass". However, in console I see the attachment exists.
Here is the output from the server console:
Started DELETE "/resources/10/delete_asset_attachment.18" for ::1 at 2019-03-09 17:27:28 -0500
Processing by ResourcesController#delete_asset_attachment as
Parameters: {"authenticity_token"=>"EFZO5V9Bii3dId0I6hn5DajFR5WJYZBc8qPAAi5ppQOFW3cws5I4FjyVP9IlvA+2a2kKUJhobnqd8atG4L3k+g==", "id"=>"10"}
Resource Load (0.1ms) SELECT "resources".* FROM "resources" WHERE "resources"."id" = ? LIMIT ? [["id", 10], ["LIMIT", 1]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
ActiveStorage::Attachment Load (0.1ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE (10) LIMIT ? [["LIMIT", 1]]
The value of #asset is #<ActiveStorage::Attachment:0x00007f8d7bd4df68>
ActiveStorage::Blob Load (0.2ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = ? LIMIT ? [["id", 27], ["LIMIT", 1]]
Completed 500 Internal Server Error in 5ms (ActiveRecord: 0.6ms)
NoMethodError - undefined method `purge' for nil:NilClass:
::1 - - [09/Mar/2019:17:27:28 EST] "POST /resources/10/delete_asset_attachment.18 HTTP/1.1" 500 76939
http://localhost:3000/resources/10/edit -> /resources/10/delete_asset_attachment.18
Started POST "/__better_errors/70da0e976a425fce/variables" for ::1 at 2019-03-09 17:27:28 -0500
ActiveStorage::Blob Load (0.2ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = ? LIMIT ? [["id", 27], ["LIMIT", 11]]
::1 - - [09/Mar/2019:17:27:28 EST] "POST /__better_errors/70da0e976a425fce/variables HTTP/1.1" 200 36499
http://localhost:3000/resources/10/delete_asset_attachment.18 -> /__better_errors/70da0e976a425fce/variables
I've searched for solutions everywhere. The couple that exist on stackoverflow didn't address my issue. There is an incredible lack of specific details and examples in the Rails guide or anywhere else on the web for specifically handling deleting attachments. Would appreciate any help.
UPDATE: Here are my routes.rb:
resources :resources do
get 'listing', :on => :collection
put :sort, on: :collection
member do
delete :delete_asset_attachment
end
end
UPDATE 2: rails routes output
resources GET /resources(.:format) resources#index
POST /resources(.:format) resources#create
new_resource GET /resources/new(.:format) resources#new
edit_resource GET /resources/:id/edit(.:format) resources#edit
resource GET /resources/:id(.:format) resources#show
PATCH /resources/:id(.:format) resources#update
PUT /resources/:id(.:format) resources#update
DELETE /resources/:id(.:format) resources#destroy

I've been able to make this work. Ahhhh. The rush, after hours of frustration.
Piecing things together after reading this article
Deleting ActiveStorage Attachments From the Controller, 3 Ways
I changed my controller code to be this:
def delete_asset_attachment
#resource.assets.find_by(params[:attachment_id]).purge
redirect_to #resource
end
and my form to be this:
<% #resource.assets.each do |asset| %>
<%= asset.filename %>
<%= link_to 'Remove Attachment', delete_asset_attachment_resource_url(#resource, asset.id), method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>
I believe the issue was that the line in my old code:
#asset = ActiveStorage::Attachment.find_by(params[:id])
...was only passing the #resource id and the attachment was not being found. The key was changing this line:
#resource.assets.find_by(params[:attachment_id]).purge
...which more properly points to the correct resource and then the specific asset (attachment) to be purged.

Fix the syntax of find_by and use safe ampersand, instead
#asset = ActiveStorage::Attachment.find_by(params[:id])
#asset.purge
try:
#asset = ActiveStorage::Attachment.find_by(id: params[:id])
#asset&.purge

Related

How to pass data from view that came from an external API to a controller?

So I want to pass a data from a view that was rendered from an external api to a controller to be able to save the ID to a model/database table.
This is my view:
<h1>Heroes</h1>
<% #response.each do |hero| %>
<div class = "hero_wrap">
<div class = "img">
<img src="https://api.opendota.com<%= hero["img"]%>", id ="hero_img"/>
</div>
<div class = "fave_container">
<%= link_to(image_tag('fave.png', class: "fave_img"), new_hero_path) %>
<%= hero["id"]%>
</div>
<div class = "hero_name">
<%= hero["localized_name"] %>
</div>
</div>
<% end %>
My controller:
# heroes_controller
class HeroesController < ApplicationController
def index
#response = HTTParty.get("https://api.opendota.com/api/heroStats")
end
def create
#hero= Hero.new(params[:id])
#hero.save
redirect_to #hero
end
end
Routes:
Rails.application.routes.draw do
get '/signup', to: 'users#new'
get 'pages/home'
resources :pro_players
devise_for :users
resources :heroes
resources :users
root 'pages#home'
end
My hero model doesn't contain anything yet, I just want to pick the hero id and save it to my database.
this is my web app
server log upon clicking the star icon with link:
Started GET "/heros?custom_hero_id=1&method=post" for 127.0.0.1 at
2018-10-15 09:20:53 +0800
Processing by HerosController#index as HTML
Parameters: {"custom_hero_id"=>"1", "method"=>"post"}
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳/home/don/.asdf/installs/ruby/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-
5.2.1/lib/active_record/log_subscriber.rb:98
Rendering heros/index.html.erb within layouts/application
Rendered heros/index.html.erb within layouts/application (42.2ms)
Rendered layouts/_header.html.erb (1.1ms)
Rendered layouts/_footer.html.erb (0.4ms)
Completed 200 OK in 770ms (Views: 83.3ms | ActiveRecord: 0.7ms)
Updated log:
Started POST "/heros?custom_hero_id=21" for 127.0.0.1 at 2018-10-15 10:13:17 +0800
Processing by HerosController#create as HTML
Parameters: {"authenticity_token"=>"PHDEXdDmPhPX+VdloU2y6yONY5HN5wI2lIfOaSbSKj9+RvTO5Ua3QPuTcreLZtGNDFPaSOXhDVyve6J69+1CQQ==", "custom_hero_id"=>"21"}
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳/home/don/.asdf/installs/ruby/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.1/lib/active_record/log_subscriber.rb:98
(0.2ms) BEGIN
↳ app/controllers/heros_controller.rb:9
Hero Create (0.4ms) INSERT INTO "heros" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", "2018-10-15 02:13:17.032248"], ["updated_at", "2018-10-15 02:13:17.032248"]]
↳ app/controllers/heros_controller.rb:9
(42.3ms) COMMIT
↳ app/controllers/heros_controller.rb:9
Redirected to http://localhost:3000/heros/10
Completed 302 Found in 59ms (ActiveRecord: 46.9ms)
Started GET "/heros/10" for 127.0.0.1 at 2018-10-15 10:13:17 +0800
AbstractController::ActionNotFound (The action 'show' could not be found for HerosController):
The :id attribute is by default a auto-increment attribute of datatype integer which sets its value under the hood whenever a new instance of the model is created. There are several ways to override the its behavior or setting its value explicitly, but considering that you are fairly new to the technology I don't recommend those for you. Instead I recommend a simpler solution of using another attribute to store hero["id"]
Steps:
1) Generate a migration to create a new column(say custom_hero_id) in the heroes table
rails g migration add_custom_hero_id_to_heroes custom_hero_id:integer
and do rake db:migrate
2) Change
<%= link_to(image_tag('fave.png', class: "fave_img"), new_hero_path) %>
to
<%= link_to(image_tag('fave.png', class: "fave_img"), heroes_path(custom_hero_id: hero["id"]), method: :post) %>
3) Finally change your create action to below
def create
#hero= Hero.create(custom_hero_id: params[:custom_hero_id])
redirect_to #hero
end
Update:
Due to unknown reason, custom_hero_id isn't saving to the DB. Probably due to forbidden attributes error. Try changing it to
def create
#hero = Hero.new(hero_params)
if #hero.save
redirect_to #hero
end
end
private
def hero_params
params.permit(:custom_hero_id)
end
Do you want to store the hero id or it's name ?
I would advise to create a method in your heroe model. Something like
// heroe.rb
def save_api_heroes_to_db(response)
response.each do |hero|
unless hero[:localized_name].exists?
hero.create(id: hero[:id], name: hero[:localized_name], image: hero[:img])
end
end
end
This method will save the heroe id, name and image in your database (unless it already exists).
You just have to call it in your index method, in your heroe controller.
Hope that helps.

Rails 5.1 strong parameters deprecated?

I'm working on using form_with in Rails 5.1.3 & Ruby 2.3.0
The best documentation I've found is here
Rails Github Repo on line 533, but it's still unclear to me.
# The parameters in the forms are accessible in controllers according to
# their name nesting. So inputs named +title+ and <tt>post[title]</tt> are
# accessible as <tt>params[:title]</tt> and <tt>params[:post][:title]</tt>
# respectively.
Code:
# friendships_controller.rb
...
private
def friendship_params
params.require(:friendship).permit(:user_id, :id)
end
# Works
def destroy
Friendship.remove_friend(current_user.id, params[:id].to_i)
end
# Doesn't work
def destroy
Friendship.remove_friend(friendship_params[:user_id].to_i, friendship_params[:id].to_i)
end
# form_with
<%= form_with model: Friendship,
url: user_friendship_path(
user_id: current_user.id
id: other_user.id,
), method: :delete do |f| %>
<button class='button'></button>
<% end %>
I know the naming is a little confusing, I still haven't figured out how to get form_with to map correctly to the route I need.
friendships#destroy located at path
/users/:user_id/friendships/:id(.:format)
resources :users do
resources :friendships
end
Error Message
Started DELETE "/users/1/friendships/8" for 127.0.0.1 at 2018-01-02 03:21:40 +0700
Processing by FriendshipsController#destroy as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"fWnYwpNDItctgg3q/TYIXy5VRO55nwQRINCCykMsDPAFBfwJKjMZ1dneNbg 5yFNHaQP+lXR4ViTje6mK+dCmVg==", "user_id"=>"1", "id"=>"8"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.2ms) BEGIN
(0.1ms) COMMIT
Friendship Load (1.0ms) SELECT "friendships".* FROM "friendships" WHERE "friendships"."friend_id" = $1 AND "friendships"."user_id" = $2 ORDER BY "friendships"."id" ASC LIMIT $3 [["friend_id", 1], ["user_id", 8], ["LIMIT", 1]]
SQL (1.2ms) DELETE FROM "friendships" WHERE "friendships"."id" = $1 [["id", 499]]
Friendship Load (0.5ms) SELECT "friendships".* FROM "friendships" WHERE "friendships"."friend_id" = $1 AND "friendships"."user_id" = $2 ORDER BY "friendships"."id" ASC LIMIT $3 [["friend_id", 8], ["user_id", 1], ["LIMIT", 1]]
SQL (0.9ms) DELETE FROM "friendships" WHERE "friendships"."id" = $1 [["id", 498]]
Completed 400 Bad Request in 13ms (ActiveRecord: 4.2ms)
ActionController::ParameterMissing (param is missing or the value is empty: friendship):
app/controllers/friendships_controller.rb:40:in `friendship_params'
app/controllers/friendships_controller.rb:22:in `destroy'
Maybe I shouldn't say it doesn't work, the behavior is how I expect with or without the friendship_params. However, the server logs returning a 400 Bad Request is a red flag to me.
Thanks for any suggestions in advance~!
The route is a nested resource, so you should provide two arguments to the path. you also need to provide the scope to give it the "friendship" key in the params.
<%= form_with scope: :friendship, url: user_friendship_path(current_user, other_user), method: :delete do |f| %>
<button class='button'></button>
<% end %>
This code is little bit confusing, I suggesting the way for simple like change your route like below
resources :users do
resources :friendships
end
Change to
resources :users
resources :friendships
and on the view
<%= link_to "Remove Friend", friendship_path(friend_id), method: :delete, data: {confirm: "Are you sure"}, class: "btn btn-xs btn-danger" %>
and finally the controller
def destroy
#friendship = current_user.friendships.where(friend_id: params[:id]).first
#friendship.destroy
flash[:notice] = "Friend was removed"
redirect_to(my_friends_path)
end
Hope to help
The answer was that the arguments to the form_with don't pass the actually parameters to the key Friendship, we have to pass them manually within the body of the form.
<%= form_with model: Friendship, url:
user_friendship_path(
current_user, other_user
), method: :delete do |f| %>
<%= f.hidden_field :id, value: other_user.id %>
<button class='button'></button>
<% end %>

Adding a button on a specific page with backlinks

First post, so i'm a newbie in StackOverflow. I'm trying for several days to make appear a Return button on a page form but only on a specific one.
So, I was advised to use backlink to make it appears.
Here's my code from the form where I want the return button
<% if #backlink.present? %>
<div class="spacer30"></div>
<% if #backlink == 'infos' %>
path = membre_path(menu: 'infos')
<% end %>
<% end %>
<%= link_to "Retour", path, class: "btn-rounded btn-turquoise btn-small" %>
Here's my code controller
def edit
super do |user|
puts "TEST PARAMS BACKLINK #{params[:backlink]}"
#backlink = params[:backlink]
end
end
and my route's :
get 'change_password', to: 'users/registrations#edit'
put 'update' => 'users/registrations#update', :as => 'user_registration'
get 'edit_password', to: 'users/registrations#edit', :as => 'user_edit'
So i should have in my log my PUTS 'TEST PARAMS BACKLINK' but nothing appear, only :
Started GET "/change_password.1?backlink=infos" for ::1 at 2017-10-04 10:07:41 +0200
Processing by Users::RegistrationsController#edit as
Parameters: {"backlink"=>"infos"}
User Load (9.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Rendering users/registrations/edit.html.erb within layouts/application
Rendered users/registrations/edit.html.erb within layouts/application (14.4ms)
Rendered shared/_navbar.html.erb (4.0ms)
Rendered shared/_flashes.html.erb (1.1ms)
Completed 200 OK in 231ms (Views: 217.0ms | ActiveRecord: 9.1ms)
Any ideas why it doesn't work?
Many thanks.
I just had to delete some lines, here's what i changed from my registration controller :
def edit
#backlink = params[:backlink]
super
end
This way, it appears exactly the way I wanted to.
Many thanks :)

Rails Custom Route Redirecting to Show

I've created a custom route make_winner_pick but every time I click the link to follow the path the controller defaults to the show action. I can't understand what I'm doing wrong and it's driving me nuts
routes.rb
resources :league_members
get "league_members/make_winner_pick" => "league_members#make_winner_pick", :as => :make_winner_pick
Where the path is called
<%= link_to 'Join League', make_winner_pick_path(league: league.id), method: :get %>
The console
Started GET "/league_members/make_winner_pick?league=3" for 127.0.0.1 at 2015-08-29 01:33:56 +0100
Processing by LeagueMembersController#show as HTML
Parameters: {"league"=>"3", "id"=>"make_winner_pick"}
User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = 2 ORDER BY "users"."id" ASC LIMIT 1
LeagueMember Load (0.5ms) SELECT "league_members".* FROM "league_members" WHERE "league_members"."id" = $1 ORDER BY "league_members"."id" ASC LIMIT 1 [["id", 0]]
Completed 404 Not Found in 5ms
ActiveRecord::RecordNotFound (Couldn't find LeagueMember with 'id'=make_winner_pick):
app/controllers/league_members_controller.rb:68:in `set_league_member'
Can anyone tell me why my custom route is not being fired and Rails is defaulting to the #show action? For some reason it appears to be looking for a league_member with an id of make_winner_pick
Thanks for looking.
Try nest your route within the resource:
resources :league_members do
collection do
get "make_winner_pick" => "league_members#make_winner_pick", :as => :make_winner_pick
end
end
Because rails recognized your route make_winner_pick as an id.
it should be:
<%= link_to 'Join League', league_members_make_winner_pick_path(league: league.id), method: :get %>

Changing boolean with link_to Error

This is what it said in my local server:
Started GET "/hunt/one" for 127.0.0.1 at 2015-04-14 23:54:47 -0700
ActiveRecord::SchemaMigration Load (0.4ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by HuntController#one as HTML
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
Rendered hunt/one.html.erb (281.8ms)
Completed 500 Internal Server Error in 406ms
ActionView::Template::Error (No route matches {:action=>"update_clue1", :controller=>"clue", :id=>nil} missing required keys: [:id]):
In my controller
def update_clue1
#clue = Clue.find(1)
#clue.update_attributes(clue1: true)
redirect_to "/home/clues"
end
In my routes
resources :clue do
member do
get :update_clue1
put :update_clue1
end
end
In my view
<%= link_to "See next clue", update_clue1_clue_path(#clue), method: :put, class: "btn btn-default" %>
Thanks for the help!
EDIT
New Error /Users/coleschiffer/Code/rp/protected-beyond-7706/app/controllers/hunt_controller.rb:14: class/module name must be CONSTANT
Here is the hunt controller
def update_clue1
#clue = Clue.find(1)
#clue.update_attributes(clue1: true)
redirect_to "/home/clues "
end

Resources