I have a problem where my controller action is creating duplicate records. I have a search form that checks the database for a record, or if its not in the database, scrapes a website to create the record. This same action (not restful i know) is also creating a record of the search itself with a value of "success" or "failure" depending on if the record could be found. However, it is consistently duplicating the record of the search. Here is the form_tag:
<%= form_tag new_search_path, remote: true, method: :get, id: 'etf-lookup-form' do %>
...
<%= text_field_tag :etf, params[:etf], placeholder: "ETF ticker symbol EX: SPY", autofocus: true, class: 'form-control search-box input-lg', style: "text-transform: uppercase" %>
and the controller:
class SearchesController < ApplicationController
def new
if params[:etf]
#etf = Etf.find_by_ticker(params[:etf].upcase)
#etf ||= Scraper.new_from_lookup(params[:etf].upcase)
end
if #etf.present?
Search.create(ticker: params[:etf].upcase, user_id: current_user.id, status: "success" )
render :js => "window.location = '#{etf_path(#etf)}'"
else
Search.create(ticker: params[:etf].upcase, user_id: current_user.id, status: "failure" )
render :js => "window.location = '#{search_path}'"
end
end
The terminal output always shows the get request being made twice before rendering the new page. How can I solve this problem? And/or what is a better way to organize these controller actions? I also have an EtfsController which contains a show action. Here are a couple fragments of from the terminal:
Started GET "/search/new?utf8=%E2%9C%93&etf=spy&button=" for ::1 at 2017-05-01 20:05:49 -0400
Processing by SearchesController#new as JS
Parameters: {"utf8"=>"✓", "etf"=>"spy", "button"=>""}
.......
Completed 200 OK in 10ms (Views: 0.1ms | ActiveRecord: 2.0ms)
Started GET "/search/new?utf8=%E2%9C%93&etf=spy&button=" for ::1 at 2017-05-01 20:05:49 -0400
Processing by SearchesController#new as JS
...
Completed 200 OK in 9ms (Views: 0.1ms | ActiveRecord: 2.7ms)
Started GET "/etfs/1" for ::1 at 2017-05-01 20:05:49 -0400
Processing by EtfsController#show as HTML
...
Completed 200 OK in 126ms (Views: 117.9ms | ActiveRecord: 1.2ms)
Probably you can refactor your code to look like this and the problem should be gone:
before_action :set_etf, only: [:new]
after_action :create_search, only: [:new]
def new
render js: window_location
end
private
def create_search
Search.create(ticker: etf_params, user_id: current_user.id, status: "success" )
end
def etf_params
#etf_params ||= params[:etf].to_s.upcase
end
def set_etf
#etf = (Etf.find_by_ticker(etf_params) || Scraper.new_from_lookup(etf_params) if etf_params.present?
end
def window_location
return "window.location = '#{etf_path(#etf)}'" if #etf.present?
"window.location = '#{search_path}'"
end
This should do the trick, let me know how it goes!
Related
So I have a 'ThaaliTakhmeens' controller where some actions have their corresponding turbo_stream templates to lazy load instances from databases using hotwire with pagination (pagy). And in those actions, there's a similar logic that I want to factor out into an after_action callback, (following the DRY principle).
After factoring out the code into an after_action, the instances don't show up on the page in fact the after_action doesn't get executed at all which I verified by giving the debugger in it. I have also provided a before_action to those actions which works perfectly fine.
Here's the code:
after_action :set_pagy_thaalis_total, only: [:complete, :pending, :all]
def complete
#tt = ThaaliTakhmeen.includes(:sabeel).completed_year(#year)
end
def pending
#tt = ThaaliTakhmeen.includes(:sabeel).pending_year(#year)
end
def all
#tt = ThaaliTakhmeen.includes(:sabeel).in_the_year(#year)
end
private
def set_pagy_thaalis_total
#total = #tt.count
#pagy, #thaalis = pagy_countless(#tt, items: 8)
debugger
end
Here's the log on visiting the 'complete' action:
Started GET "/takhmeens/2022/complete" for ::1 at 2023-01-21 10:07:35 +0530
Processing by ThaaliTakhmeensController#complete as HTML
Parameters: {"year"=>"2022"}
Rendering layout layouts/application.html.erb
Rendering thaali_takhmeens/complete.html.erb within layouts/application
Rendered shared/_results.html.erb (Duration: 2.4ms | Allocations: 2088)
Rendered thaali_takhmeens/complete.html.erb within layouts/application (Duration: 3.7ms | Allocations: 2396)
Rendered layout layouts/application.html.erb (Duration: 3.9ms | Allocations: 2477)
Completed 500 Internal Server Error in 6ms (ActiveRecord: 0.0ms | Allocations: 3027)
ActionView::Template::Error (undefined method `any?' for nil:NilClass
'.freeze; if instances.any?
^^^^^):
1: <%= turbo_frame_tag :results, data: { turbo_action: "advance" } do %>
2: <div class="container text-center mt-5">
3: <% if instances.any? %>
4: <%= render partial: "theader" %>
5: <div id=<%="#{id}"%> ></div>
6: <%= turbo_frame_tag :pagination, loading: :lazy, src: path %> %>
app/views/shared/_results.html.erb:3
app/views/shared/_results.html.erb:1
app/views/thaali_takhmeens/complete.html.erb:8
Since the after_action callback doesn't run, the instances (#thaalis) object is not set hence this error shows up, and also debugger didn't get executed either.
Here the complete action has both HTML and turbo_steam templates. And just to be clear that the content loads perfectly fine without the need for an after_action callback but that would be against the DRY principle.
So what would be the workaround for this? Is there another way to refactor the code or should I have to set something explicitly in the callback method to get it executed?
Good question. I actually not use after_action often and had to check. https://guides.rubyonrails.org/action_controller_overview.html#after-filters-and-around-filters What I think happen is that the rendering of the view is part of the action.
In your case this is inferred, you have no respond to block like this:
respond_to do |format|
if #record.update(record_params)
format.html { redirect_to a_record_route }
else
format.html { render :edit, status: :unprocessable_entity }
end
end
But the rendering of the template still happens in the action. Before you set some instance variables useful to the view.
What you can do if you really want to dry up your controller is adding set_pagy_thaalis_total at the end of each of your actions and removing the after_action.
EDIt: Also the fact your view is an html.erb or turbo_stream.erb file doesn't really matter.
In very short.
When I have a form set as POST, the controller picks up the request, processes it and even starts to render the correct view. But the browser stays on the form page
When I switch the form to GET, it works
(yes, I remembered to change the route from get to post and back)
Here is the log:
Started POST "/sooth/search" for 127.0.0.1 at 2022-07-02 13:43:40
-0700 Processing by SoothController#search as TURBO_STREAM Parameters: {"authenticity_token"=>"[FILTERED]",
"search"=>{"name"=>"search keywords"}, "commit"=>"Save Search"}
Rendering layout layouts/application.html.erb Rendering
sooth/search.html.erb within layouts/application
I am rendering the search html page
The line above is a log message in the search.html.erb page
Rendered sooth/search.html.erb within
layouts/application (Duration: 1.0ms | Allocations: 148) Rendered
layout layouts/application.html.erb (Duration: 6.6ms | Allocations:
2710) Completed 200 OK in 15ms (Views: 11.3ms | ActiveRecord: 0.0ms |
Allocations: 4040)
BUT the search page is not displayed. Browser stays on the search form page.
Any hints deeply appreciated.
(And as you have probably guessed, I am day 1 with rails)
EDIT:
class SoothController < ApplicationController
include SoothHelper
def index
puts "sooth index"
template = get_query_template('sooth_search')
puts(template)
end
def search
form_params = params[:search]
puts 'searching' + form_params[:name].to_s
render "sooth/search"
end
end
ROUTES
Rails.application.routes.draw do
Rails.application.routes.draw do
get "/nlp_data", to: "nlp_data#index"
get "/sooth", to: "sooth#index"
post "/sooth/search", to: "sooth#search"
end
Your problem is you are trying to render the same page instead of redirecting to to the sooth page ,and secondly you cannot acces params directly in a post request, instead you must acces it from a strong param method
class SoothController < ApplicationController
include SoothHelper
def index
puts "sooth index"
template = get_query_template('sooth_search')
puts(template)
end
def search
form_params = sooth_params[:search]
puts 'searching' + form_params[:name].to_s
redirect_to "/sooth"
end
private
def sooth_params
params.require(:sooth).permit(:search)
end
end
I m trying to upload an image with tinymce and rails 7 (active storage and tinymce-rails gem)
My controller image :
class UploaderController < ApplicationController
skip_forgery_protection
def image
blob = ActiveStorage::Blob.create_after_upload!(
io: params[:file],
filename: params[:file].original_filename,
content_type: params[:file].content_type
)
render json: {location: url_for(blob)}, content_type: "text / html"
end
end
My model controller (protected section) :
class Back::ThemesController < BackController
before_action :theme, only: [:edit, :update, :destroy]
def new
#theme = Theme.new
end
def create
#theme = Theme.new(theme_params)
if #theme.save
redirect_to admin_themes_path
else
render :new
end
end
My tinymce config :
toolbar:
- file edit view insert format tools
plugins:
- image
images_upload_url: '/uploader/image'
My routes :
post ‘uploader/image’, to: ‘uploader#image’
And in my form :
<%= tinymce %>
<%= f.input :description, as: :text, input_html: { class: "tinymce" , rows: 40, cols: 120 } %>
When I try to upload an image in my back office, tinymce return a wrong back url (http://localhost:3000/uploader/image ) and I get a routing error :
Started POST "/uploader/image" for ::1 at 2021-11-14 12:53:17 +0100
Processing by UploaderController#image as */*
Parameters: {"file"=>#<ActionDispatch::Http::UploadedFile:0x00007fd46f8d2b88 #tempfile=#<Tempfile:/var/folders/_r/ytzkgwbd15jfz9w_csgk1jn00000gn/T/RackMultipart20211114-53999-wsms1f.jpg>, #original_filename="80s.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"file\"; filename=\"80s.jpg\"\r\nContent-Type: image/jpeg\r\n">}
Completed 200 OK in 2ms (Views: 0.2ms | ActiveRecord: 0.0ms | Allocations: 892)
Started GET "/uploader/image" for ::1 at 2021-11-14 12:53:17 +0100
ActionController::RoutingError (No route matches [GET] "/uploader/image"):
Here is the live demo
I'm trying to show listbox using remote actions just searching params, but is not working.
Clients_controller.rb
def new
#client.new
end
def search_city
#cities = City.where('id= ?',params[:city_id])
respond_to do |format|
format.js
end
end
search_city.js.erb
$("#test").html("<%= escape_javascript(render(:partial => "city_results"))%>");
Models
Client.rb
belongs_to :city
City.rb
belongs_to :state
has_many :clients
State.rb
has_many :cities
Routes.rb
resources :clients do
match 'search_city',:via =>[:get], :on => :collection
end
_form.erb
<%= link_to "Get list cities", {:action => "search_city"}, :remote => true, :class => "button-link" %>
<div id = "test">Here this text will be replaced</div>
_city_results.erb
<%= select_tag "example",options_for_select(#cities.collect { |p| [p.name,p.id] }) %>
Here complete log:
Started GET "/clients/new" for 127.0.0.1 at 2016-04-14 17:31:03 -0500
Processing by ClientsController#new as HTML
Completed 200 OK in 133ms (Views: 107.4ms | ActiveRecord: 1.0ms)
Rendered client_management/clients/new.html.erb within layouts/application (21.6ms)
Started GET "/clients/search_city?city_id=1" for 127.0.0.1 at 2016-04-14 16:43:55 -0500
Processing by ClientManagement::ClientsController#search_city as JS
Parameters: {"city_id"=>"1"}
Province Load (0.2ms) SELECT `states`.* FROM `states` WHERE `states`.`city_id` = 1
Rendered clients/partials/_city_results.erb (1.9ms)
Rendered clients/search_city.js.erb (2.9ms)
Completed 200 OK in 7ms (Views: 4.7ms | ActiveRecord: 0.4ms)
INFO
SELECT `states`.* FROM `states` WHERE `states`.`city_id` = 1
RESULT: 1 (exist value)
PROBLEM:
Is not showing the select tag with options.
When I check if params are sended everything is ok but in the view is not replaced.
Need to install a gem?
Is just working with simple text without but need rails code.
I tried this:
$('#province').html("<%= escape_javascript( render :partial => 'clients/partials/city_results' ) %>");
$('#province').show();
def search city
....
respond_to do |format|
format.js
end
end
I'm working through Ruby on Rails 4 Essential Training from Lynda.com and I'm running in this error:
ActionController::ParameterMissing in SubjectsController#create
I basically have a form with 3 inputs, :name, :position, and :visible. I can enter the the information just fine but then I get the error when I hit the submit button. Here is the output from the server:
`Started POST "/subjects/create" for 127.0.0.1 at 2014-11-11 18:02:12 -0500
Processing by SubjectsController#create as HTML
Parameters: {"utf8"=>"√", "authenticity_token"=>"f6+AdWN3jWO6mL9jrPDgVGoAm/NTBF1GPxGasTaqMh0=", "subject"=>{"name"
=>"Default", "position"=>"5", "visible"=>"false"}, "commit"=>"Create Subject"}
(0.0ms) BEGIN
SQL (1.0ms) INSERT INTO `subjects` (`created_at`, `name`, `position`, `updated_at`) VALUES ('2014-11-11 23:02:12'
, 'Default', 5, '2014-11-11 23:02:12')
(4931.3ms) COMMIT
Redirected to http://localhost:3000/subjects/create?actions=index
Completed 302 Found in 4941ms (ActiveRecord: 4932.3ms)
Started GET "/subjects/create?actions=index" for 127.0.0.1 at 2014-11-11 18:02:17 -0500
Processing by SubjectsController#create as HTML
Parameters: {"actions"=>"index"}
Completed 400 Bad Request in 1ms
ActionController::ParameterMissing (param is missing or the value is empty: subject):
app/controllers/subjects_controller.rb:40:in `subject_params'
app/controllers/subjects_controller.rb:18:in `create'
SubjectsController
class SubjectsController < ApplicationController
layout false
def index
#subjects = Subject.sorted
end
def show
#subject = Subject.find(params[:id])
end
def new
#subject = Subject.new({:name => "Default"})
end
def create
#subject = Subject.new(subject_params)
if #subject.save
redirect_to(:actions => 'index')
# raise params.inspect
else
render('new')
end
end
def edit
end
def delete
end
private
def subject_params
# same as using "params[:subject]", except that it:
# - raises an error if :subject is not present
# - allows listed attributes to be mass-assigned
params.require(:subject).permit(:name, :position, :visible)
end
end
Looking at the server output it doesn't look like I'm passing the :visible attribute to the database even though the data shows up on the index page even after the error.
I don’t think :visible is the problem, looking at your logs, you are getting this error:
ActionController::ParameterMissing (param is missing or the value is empty: subject):
app/controllers/subjects_controller.rb:40:in `subject_params'
app/controllers/subjects_controller.rb:18:in `create'
This is because of this line:
Started GET "/subjects/create?actions=index" for 127.0.0.1 at 2014-11-11 18:02:17 -0500
Processing by SubjectsController#create as HTML
Parameters: {"actions"=>"index"}
Looks like you are redirecting to GET /subjects/create and passing in parameters actions=index, likely caused by this line in your controller:
redirect_to(:actions => 'index')
I think what you meant here was really :action (singular):
redirect_to(:action => 'index')
Regarding the issue with :visible not being saved, I can’t say much without knowing anything about the model, has visible been declared in the database schema?