Data is not being saved in Rails app - ruby-on-rails

I am trying to save data, but on submitting the data, it just refreshes the page, but not actually stores data in table. and even it is not giving any errors too. What I want to do is after saving data, control should come on same page with empty textboxes.
Using Ruby 2.4.1, Rails 5.1.3, sqLite3
This is content of feedback_controller.rb
class FeedbackController < ApplicationController
def new
#feedback = Feedback.new
end
def create
#feedback = Feedback.new(feedback_params)
redirect_to root_path
respond_to do |f|
if #feedback.save
f.html {redirect_to #feedback, notice: 'feedback submitted'}
f.json {render :new, status: :created, location: :#feedback}
else
f.html {render :new }
f.json {render json: #feedback.errors, status: :unprocessable_entity}
end
end
end
private
# def set_feedback
# #feedback = Feedback.find(params[:id])
# end
def feedback_params
params.require(:feedback).permit(:name, :email, :message)
end
end
And here is new.html.erb file.
<%= form_with scope: :feedback, url: feedback_new_path, local: true do |form| %>
<% if #feedback.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(feedback.errors.count, "error") %> prohibited this feedback from being saved:</h2>
<ul>
<% #feedback.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :name %>
<%= form.text_field :name, id: :feedback_name %>
</div>
<div class="field">
<%= form.label :email %>
<%= form.text_field :email, id: :feedback_email %>
</div>
<div class="field">
<%= form.label :message %>
<%= form.text_area :message, id: :feedback_message %>
</div>
<div class="actions">
<%= form.submit "Submit Feedback"%>
</div>
<% end %>
<%= link_to 'Home', root_path %>
This is what puma server saying on log while storing data:
Started POST "/feedback/new" for 127.0.0.1 at 2017-09-14 21:33:41 +0530
Processing by FeedbackController#new as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Dk1sGiKYryIOeU6HyO7zeX6wqXazj9BUfooJgIDH1JjHxuKwA5MT0E6zvuwFYXGA8xEOz+tGAiXWySKv7voKvg==", "feedback"=>{"name"=>"sheshang", "email"=>"sid#gm.co", "message"=>"this is demo message"}, "commit"=>"Submit Feedback"}
Rendering feedback/new.html.erb within layouts/application
Rendered feedback/new.html.erb within layouts/application (3.8ms)
Completed 200 OK in 75ms (Views: 55.2ms | ActiveRecord: 1.3ms)
In addition to this problem, I want to know that how to debug in rails app. Like in C or any other program we used to put printf statements to check whether the control is going inside or not. How we can do in Rails?

As you can see, you're posting to the new action:
Started POST "/feedback/new" for 127.0.0.1 at 2017-09-14 21:33:41 +0530
Processing by FeedbackController#new as HTML
But the new action doesn't do a save.
So, you probably want:
<%= form_with scope: :feedback, url: feedbacks_path, local: true do |form| %>
By convention, if you do (in your routes.rb file):
resources :feedbacks
then you should get:
feedbacks GET /feedbacks(.:format) feedbacks#index
POST /feedbacks(.:format) feedbacks#create
new_feedback GET /feedbacks/new(.:format) feedbacks#new
edit_feedback GET /feedbacks/:id/edit(.:format) feedbacks#edit
feedback GET /feedbacks/:id(.:format) feedbacks#show
PATCH /feedbacks/:id(.:format) feedbacks#update
PUT /feedbacks/:id(.:format) feedbacks#update
DELETE /feedbacks/:id(.:format) feedbacks#destroy
As you can see, this does not generate a POST verb for the new action, which suggests that you're not using conventional routes. If that is true, then you may need to update your routes so that you get feedbacks_path.
I recommend following convention as it often makes life easier.
As far as printing to console, I tend to do:
puts "#{self.class}.#{__method__}, var: #{var}"
I replace var with whatever variable I want to inspect. I like to include the #{self.class}.#{__method__} bit because it let's me know which class and method I'm in.

Related

How to edit a single attribute inline with Turbo Frame and Trubo Stream with validation feedback?

Creating In-Place-Editing of a single attribute of a model using Turbo Frames (not using a gem such as Best_In_Place as it requires jQuery and is not working well with Rails 7) This implemenation is using ONLY turboframes.
To accomplish this I followed this tutorial: https://nts.strzibny.name/single-attribute-in-place-editing-turbo/ (written in January 2022)
The tutorial does not match Ruby 3.2.0, Rails 7.0.4 perfectly and needs a one variable adjustment on the show page to work.
Unfortunately, there is no validation feedback currently in this tutorials method as the turbo_frame form implemented does not have it included.
Question: how to properly add validation feedback and routing of errors? (preferably a turbo_frames only solution)
Summary of tutorial:
create new app and scaffold one model: User name:string
changes to UsersController (a new action on the controller to edit a single attribute, and adding edit_name to before_action list)
before_action :set_user, only: %i[ show edit edit_name update destroy ]
# GET /users/1/edit_name
def edit_name
end
add to routes.rb (a new route for editing a single specific attribute)
resources :users do
member do
get 'edit_name'
end
end
create view/users/edit_name.html.erb (a new view page to support editing a specific attribute, (here a name)).
<%= turbo_frame_tag "name_#{user.id}" do %>
<%= form_with model: #user, url: user_path(#user) do |form| %>
<%= form.text_field :name %>
<%= form.submit "Save" %>
<% end %>
<% end %>
additions on _user.html.erb file (the link to the created turbo frame form edit_name.html.erb)
<%= turbo_frame_tag "name_#{user.id}" do %>
Name: <%= link_to #user.name, edit_name_user_path(#user) %>
<% end %>
Upon starter the app server I get errors about #user being nil:Class.
In order to get the tutorial to work I have to change the _user.html.erb file to use a local variable for user in the link.
edited again  _user.html.erb (changing instance variable #user to local variable user)
<%= turbo_frame_tag "name_#{user.id}" do %>
Name: <%= link_to user.name, edit_name_user_path(user) %>
<% end %>
With this change, the tutorial works, allowing single attribute in place editing through turbo frames! But no model validation feedback is implemented.
Below, I attempt to deal with validation, first adding validation to models/user.rb
class User < ApplicationRecord
validates :name, presence: true
validates :name, comparison: { other_than: "Jason" }
end
PROPOSED SOLUTION:
CREATE a new turbo_stream file for editing errors that pop up (it has an error in the turbo_frame tag that it is targeting, it needs to be able to target any parent turboframe where the single attribute edit was initiated)
<%= turbo_stream.replace"name_#{#user.id}" do %>
<%= form_with model: #user, url: user_path(#user) do |form| %>
<% if #user.errors.any? %>
<div style="color: red">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<% if #user.errors[:name].any? %>
<%= form.label :name, style: "display: block" %> <%= form.text_field :name %>
<% end %>
<% if #user.errors[:active].any? %>
<%= form.label :active, style: "display: block" %> <%= form.check_box :active %>
<% end %>
<%= form.submit "Save" %>
<% end %>
<% end %>
and edit the UsersController.rb update method to deal with turbo stream errors
# PATCH/PUT /users/1 or /users/1.json
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to user_url(#user), notice: "User was successfully updated." }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #user.errors, status: :unprocessable_entity }
format.turbo_stream do
if #user.errors[:name].any?
#user.name = nil #so that it does not repopulate the form with the bad data
if #user.errors[:active].any?
#user.active = nil
end
render :edit_errors, status: :unprocessable_entity
end
end
end
end
This all works except for after entering a succesful edit on the form produced after an invalid entry, it renders the show for that entry only, rather than all of them.
What would be a 'dry'er method of doing all of this? (and how do I target updating just the one frame from the turbo stream so that only the one field gets updated after success on validation)?
Philosophically, is any of this worth it now compared to just using jQuery and the Gem Best_In_Place??? Seems like the number of changes are piling up and the code will get ugly if supporting such functionality across multiple attributes?
Since the initial issue is resolved, I'll just add some other ways you can do this. It's gonna be a little more work to do this yourself and you won't have all the functionality that some gem could give you. On the other hand, it's a lot less code and you have full control over everything. Besides, if you just need to have this one field to be editable, installing a gem and jquery is too much overhead.
Setup:
# rails v7.0.4.2
$ rails new hello_edit_in_place -c tailwind
$ cd hello_edit_in_place
$ bin/rails g scaffold User email first_name last_name --skip-timestamps
$ bin/rails db:migrate
$ bin/rails runner "User.create(email: 'admin#localhost', first_name: 'super', last_name: 'admin')"
$ open http://localhost:3000/users
$ bin/dev
class User < ApplicationRecord
validates :email, presence: true, length: {minimum: 3}
end
Turbo Frame
I'll just modify the default form and won't touch the controller as a quick example:
# app/views/users/_form.html.erb
# NOTE: this lets you render this partial and pass a local `:attribute` or
# get attribute from url params.
<% if attribute ||= params[:attribute] %>
<%= turbo_frame_tag dom_id(user, attribute) do %>
# NOTE: send `attrtibute` back in case of validation error, so this page
# can be rendered again with params[:attribute] set.
# V
<%= form_with model: user, url: user_path(user, attribute:) do |f| %>
<%= f.text_field attribute %>
# NOTE: show validation errors
<%= safe_join user.errors.full_messages_for(attribute), tag.br %>
<%= f.submit "save" %>
<% end %>
<% end %>
<% else %>
# original form here
<% end %>
# app/views/users/_user.html.erb
# NOTE: there is no need to have the whole set up for each individual
# attribute
<% user.attribute_names.reject{|a| a =~ /^(id|something_else)$/}.each do |attribute| %>
<%= tag.div attribute, class: "mt-4 block mb-1 font-medium" %> # tag.div - so that i can keep rb syntax highlight for stackoverflow
<%= turbo_frame_tag dom_id(user, attribute) do %>
<%= link_to edit_user_path(user, attribute:) do %>
<%= user.public_send(attribute).presence || "—".html_safe %>
<% end %>
<% end %>
<% end %>
That's it, every attribute is rendered, is editable and email shows validation errors. Also because all turbo_frame_tags have a unique id, everything works with multiple users on the index page.
Turbo Stream
You can also use turbo_stream to have more flexibility and make it even more dynamic, but it's a bit more of a set up. Also, add ability to edit full name in place, with first_name and last_name fields together:
# config/routes.rb
# NOTE: to not mess with default actions, add new routes
resources :users do
member do
get "edit_attribute/:attribute", action: :edit_attribute, as: :edit_attribute
patch "update_attribute/:attribute", action: :update_attribute, as: :update_attribute
end
end
# app/views/users/_user.html.erb
# Renders user attributes.
# Required locals: user.
<%= render "attribute", user:, attribute: :email %>
<%= render "attribute", user:, attribute: :name %>
# app/views/users/_attribute.html.erb
# Renders editable attribute.
# Required locals: attribute, user.
<%= tag.div id: dom_id(user, attribute) do %>
<%= tag.div attribute, class: "mt-4 block mb-1 font-medium" %>
# NOTE: to make a GET turbo_stream request vvvvvvvvvvvvvvvvvvvvvvvvvv
<%= link_to edit_attribute_user_path(user, attribute:), data: {turbo_stream: true} do %>
# far from perfect, but gotta start somewhere
<% if user.attribute_names.include? attribute.to_s %>
<%= user.public_send(attribute) %>
<% else %>
# if user doesn't have provided attribute, try to render a partial
<%= render attribute.to_s, user: %>
<% end %>
<% end %>
<% end %>
# app/views/users/_name.html.erb
# Renders custom editable attribute value.
# Required locals: user.
<%= user.first_name %>
<%= user.last_name %>
# app/views/users/_edit_attribute.html.erb
# Renders editable attribute form.
# Required locals: attribute, user.
<%= form_with model: user, url: update_attribute_user_path(user, attribute:) do |f| %>
<% if user.attribute_names.include? attribute.to_s %>
<%= f.text_field attribute %>
<% else %>
# NOTE: same as before but with `_fields` suffix,
# so this requires `name_fields` partial.
<%= render "#{attribute}_fields", f: %>
<% end %>
<%= f.submit "save" %>
<% end %>
# app/views/users/_name_fields.html.erb
# Renders custom attribute form fields.
# Requires locals:
# f - form builder.
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
# app/controllers/users_controller.rb
# GET /users/:id/edit_attribute/:attribute
def edit_attribute
attribute = params[:attribute]
respond_to do |format|
format.turbo_stream do
# render form
render turbo_stream: turbo_stream.update(
helpers.dom_id(user, attribute),
partial: "edit_attribute",
locals: {user:, attribute:}
)
end
end
end
# PATCH /users/:id/update_attribute/:attribute
def update_attribute
attribute = params[:attribute]
attribute_id = helpers.dom_id(user, attribute)
respond_to do |format|
if user.update(user_params)
format.turbo_stream do
# render updated attribute
render turbo_stream: turbo_stream.replace(
attribute_id,
partial: "attribute",
locals: {user:, attribute:}
)
end
else
format.turbo_stream do
# render errors
render turbo_stream: turbo_stream.append(
attribute_id,
html: (
helpers.tag.div id: "#{attribute_id}_errors" do
# FIXME: doesn't render `first_name` `last_name` errors
helpers.safe_join user.errors.full_messages_for(attribute), helpers.tag.br
end
)
)
end
end
end
end
private
def user
#user ||= User.find(params[:id])
end

Javascript don't substitute html in rails + Ajax

Still learning how to use Ajax in Rails.
I have a simple controller:
class PagineController < ApplicationController
def cerca
end
def trova
respond_to do |format|
format.js {render layout: false }
end
end
end
routes.rb:
get '/cerca', to: 'pagine#cerca'
post '/cerca', to: 'pagine#trova'
This is trova.js.erb:
$('#search_table').html("<%= escape_javascript(render partial: 'search_result') %>");
This is cerca.html.erb:
<%= form_tag '/cerca', remote: true do %>
<%= text_field_tag :name, params[:name], placeholder: 'Inserisci parte
del nome del gioco' %>
<%= submit_tag 'Cerca' %>
<% end %>
<h1> Results </h1>
<div id="search_table">
<p>Here the partial</p>
</div>
and this is the _search_result.html.erb
<p> I'm running well </p>
I'm expecting that, when i submit a search with the button, "Here the partial" will be substituted with "I'm running well".
But this is not happens.
In the log i see that _search_result.html.erb is correctly rendered, so all should run.
Why don't?
Ok. I've resolved (for future references if someone needs), using different js:
document.getElementById("search_table").innerHTML = "<%= j render 'search_result' %>"

How to pass parameter not related to model by form_for on rails?

I'd like to pass parameter not related to model by form_for on rails.I want to pass 'address' data, that is not related to product model, from view to controller. The following is my code, however it returned the next error. Could you tell me how to solve this problem?
Completed 500 Internal Server Error in 11ms (ActiveRecord: 0.1ms)
ActionView::Template::Error (wrong number of arguments (1 for 2..5)):
products_controller
class ProductsController < ApplicationController
~~~~
def transfer
#product = Product.new
end
def transfer_done
my_address = params[:my_address]
wallet = Wallet.find_by(address:my_address)
redirect_to root_url
end
~~~~
end
transfer.html.erb
<% provide("transfer") %>
<p>
<%= form_for #product, :url => {:action => 'transfer_done'} do |f| %>
<div>
<%= check_box :my_address %> <---Error occurred here.
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
</p>
You may try <%= text_field_tag :my_address %> inside your form

Ruby on rails: create action doesn't work, while new, edit and update (the same form as in create !) actions works - why?

I have Realization model:
# encoding : utf-8
class Realization < ActiveRecord::Base
attr_accessible :city, :street, :title, :work, :photo, :date
has_attached_file :photo
end
Controller:
# encoding : utf-8
class RealizationsController < ApplicationController
before_filter :admin_required, :except => [:index,:show]
# GET /realization/new
def new
#realization = Realization.new
#realization.date = Time.now.__send__(:to_date).to_s
end
# POST /realization
def create
#realization = Realization.new(params[:realization])
if #realization.save
redirect_to #realization, notice: 'realization was successfully created.'
else
render action: "new"
end
end
(...) others
View of form:
<%= form_for #realization, :html => { :multipart => true } do |f| %>
<% if #realization.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#realization.errors.count, "error") %> prohibited this realization from being saved:</h2>
<ul>
<% #realization.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br />
<%= f.text_field :title %>
</div>
(...)
<div class="field">
<%= f.file_field :photo %>
</div>
<div class="actions">
<%= f.submit "Submit" %>
</div>
<% end %>
And routes :
resources :realizations
And WEBrick server info is that:
Started POST "/realizacje" for 127.0.0.1 at 2013-04-12 12:26:35 +0200
Processing by RealizationsController#index as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"zK5jP4ChBBY+R21TjrZkp4xGvCHViTFJ+8Fw6Og28YY=", "realization"=>{"title"=>"wwwwww", "street"=>"", "city"=>"", "work"=>"", "date"=>"2013-04-12"}, "commit"=>"Submit"}
(1.0ms) SELECT COUNT(*) FROM "realizations"
Realization Load (2.0ms) SELECT "realizations".* FROM "realizations" ORDER BY created_at DESC LIMIT 7 OFFSET 0
User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Rendered realizations/index.html.erb within layouts/application (156.0ms)
Completed 200 OK in 340ms (Views: 333.0ms | ActiveRecord: 4.0ms)
While I use the form and push the submit it redirects/randers realizations/index without notice or errors even!
I have completely no idea why? Especialy that it worked before...
Maybe javascript added later on may be the reason?
Paperclip works well in update so it isn't it...
You might check your new action to see what you're passing in to the form_for.
You want to be passing in a brand new instance of your Realization model.
i.e. in the new action you should have a line that reads #realization = Realization.new
The reason I suggest this is because form_for calls a method (#new_record?) on the object you give it and will submit a post or put request depending on whether that method call returns true or false.

RoR appended ?format= on form submit

I'm creating a little newsletter application, with 'double opt-in restrictions', when I simply fill in my form (subscription page) and submit the form I get redirected to my subscribed page (which is all normal) however my form appends a querystring to my action attribute of my form (http://localhost:3000/newsletter/subscribe?format=)
routes:
match 'newsletter/subscription' => 'newsletter_subscriptions#subscription'
post 'newsletter/subscribe' => 'newsletter_subscriptions#subscribe'
controller:
class NewsletterSubscriptionsController < ApplicationController
respond_to :html
# GET /newsletter/subscription
def subscription
respond_with (#subscription = NewsletterSubscription.new)
end
# POST /newsletter/subscribe
def subscribe
# If there's already an unconfirmed record with the submitted email, use that object otherwise create a new one based on the submitted email
sub_new = NewsletterSubscription.new
sub_new.email = params[:newsletter_subscription]['email']
sub_old = NewsletterSubscription.find_by_email_and_confirmed sub_new.email, 0
#subscription = sub_old || sub_new
if #subscription.save
Newsletter.delay.subscribed(#subscription) # with delayed_job
else
render :action => "subscription"
end
end
...
end
view (newsletter_subscription/subscription.html.erb):
<h1>New newsletter_subscription</h1>
<%= form_for(#subscription, :url => newsletter_subscribe_path(#subscription)) do |f| %>
<% if #subscription.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#subscription.errors.count, "error") %> prohibited this newsletter_subscription from being
saved:</h2>
<ul>
<% #subscription.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :email %>
<br/>
<%= f.text_field :email %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
PS: I would be pleased if someone could evaluate my ruby code please (posted above), I'm still learning a lot and would like to see some 'guidelines' or feedback, I think I still can learn a lot.
Try removing the #subscription argument you're passing into newsletter_subscribe_path. Since there isn't an :id in the route and it's a new object, passing it doesn't really make sense. I'm assuming that's what is being interpreted as the format.
<%= form_for(#subscription, :url => newsletter_subscribe_path) do |f| %>
As for improvements you can make to the code, the biggest thing I see is moving the old/new subscription logic into the model.
# in NewsletterSubscription
def self.with_email(email)
find_by_email_and_confirmed(email, 0) || new(:email => email)
end
# in controller
#subscription = NewsletterSubscription.with_email(params[:newsletter_subscription]['email'])
if #subscription.save
#...
Also respond_to and respond_with aren't really necessary here since you're just dealing with HTML views. You can remove that.

Resources