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.
Related
I created a button where users can input stuff in a field and then press the button to update the database (put request) which can be seen here in show.html.erb:
<% provide(:title, #user.name) %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
<br>
<%= #user.email %>
<% if #errors %>
<p>THE FORM COULD NOT BE SAVED </p>
<ul id='errors'>
<% #errors.each do |error| %>
<li><%= error %></li>
<% end %>
</ul>
<% end %>
<br>
<% if is_admin? %>
<% if !#user.admin %>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#user) do |f| %>
<%= f.label :wistia_project_id %>
<%= f.text_field :wistia_project_id, class: 'form-control' %>
<%= f.submit "Save", :action => "set_wistia_project_ID", :method => :patch, :form_class => "form-control" %>
<% end %>
</div>
</div>
<% end %>
<% end %>
</h1>
</section>
</aside>
</div>
The function is in user_controller.rb:
# Sets wistia_project_ID.
def set_wistia_project_ID
#user = User.find(params[:id])
#user.set_project_id
unless #user.valid?
#errors = #user.errors.full_messages
render :show
end
end
That function calls another function, just to separate things more clearly. This other function lives in user.rb:
# Sets the wistia_project_ID.
def set_project_id!(val)
self.wistia_project_ID = val # self is necessary here
save # or self.save, but the self is unnecessary here
end
My routes.rb:
.
.
.
resources :users do
member do
patch 'set_wistia_project_ID'
end
end
My problem is that right now, when you press the button, it says: Completed 500 Internal Server Error in 26ms (ActiveRecord: 0.7ms)
and
NoMethodError (undefined method `set_project_id' for #<User:0x000055b1a0914ab8>
2019-06-26T14:46:34.940086+00:00 app[web.1]: Did you mean? wistia_project_id):
Zavitoski got it right. I suggest, however, that you're doing a number of things more fundamentally wrong. Given that you're early in your rails journey, I hope you don't mind if I point a few things out.
First, and to be nit-picky, yes, you created a button. But, it is not a button "where users can input stuff in a field and then press the button to update the database". You created a button on a form. And you created a field on that form. The user can input stuff into the field. And when clicked, the button submits the form which includes the information in the field.
Now, on that form, you did:
<%= form_for(#user) do |f| %>
<%= f.label :wistia_project_id %>
<%= f.text_field :wistia_project_id, class: 'form-control' %>
<%= f.submit "Save", :action => "set_wistia_project_ID", :method => :patch, :form_class => "form-control" %>
<% end %>
There are a few things wrong with:
:action => "set_wistia_project_ID"
First, set_wisteria_project_ID is not a very ruby-ish action name. set_wistia_project_id would be more like it. Also, you're using old-form key-value formatting. And, you can use a symbol instead of a string for your action name so your code is prettier. Something, perhaps, like:
<%= f.submit "Save", action: :set_wistia_project_id, method: :patch, form_class: "form-control" %>
But, that's a mistake, too. Because you don't need a set_wistia_project_id action. (It's an action or a method, not a function.) You already have the update action. And form_for is smart enough to submit to this action if #user is an instance of User. So, really, you should do:
<%= form_for #user do |f| %>
<%= f.label :wistia_project_id %>
<%= f.text_field :wistia_project_id, class: 'form-control' %>
<%= f.submit "Save", form_class: "form-control" %>
<% end %>
I'm not sure what form_class is, but I'll trust that it's correct.
Now, in your UsersController, just do:
class UsersController < ApplicationController
def update
#user = User.find(params[:id])
if user.update(user_params)
# do something successful
else
# do something unsuccessful
end
end
private
def user_params
# NOTE: You'll probably want to permit other stuff here, too.
params.require(:user).permit(:wistia_project_id)
end
end
Get rid of this:
class User < ApplicationRecord
# Sets the wistia_project_ID.
def set_project_id!(val)
self.wistia_project_ID = val # self is necessary here
save # or self.save, but the self is unnecessary here
end
end
Because you're just duplicating the update method. And, you probably want that attribute to be wistia_project_id, not wistia_project_ID. (Again, you never see _ID as the suffix in rails core and you might as well be conventional.) And, if you make sure you have your association set up correctly, ActiveRecord should make sure that wistia_project_id is actually a valid value.
And write your routes.rb like this:
resources :users
Because you don't need all that set_wistia_project_id business.
It appears that you are not calling the function by the name you defined, neither passing the parameter (project_id) needed.
def set_wistia_project_ID
#user = User.find(params[:id])
#user.set_project_id!(params[:wistia_project_id])
unless #user.valid?
#errors = #user.errors.full_messages
render :show
end
end
This should use the function you created and pass the parameter from the form.
Two models, Organization and User, have a 1:many relationship. I have a combined signup form where an organization plus a user for that organization get signed up.
The problem I'm experiencing is: When submitting invalid information for the user, it renders the form again, as it should, but the error messages (such as "username can't be blank") for the user are not displayed. The form does work when valid information is submitted and it does display error messages for organization, just not for user.
How should I adjust the code below so that also the error messages for user get displayed?
def new
#organization = Organization.new
#user = #organization.users.build
end
def create
#organization = Organization.new(new_params.except(:users_attributes)) #Validations require the organization to be saved before user, since user requires an organization_id. That's why users_attributs are above excluded and why below it's managed in a transaction that rollbacks if either organization or user is invalid. This works as desired.
#organization.transaction do
if #organization.valid?
#organization.save
begin
# I executed next line in debugger (with invalid user info), which correctly responds with: ActiveRecord::RecordInvalid Exception: Validation failed: Email can't be blank, Email is invalid, Username can't be blank, etc.
#organization.users.create!(users_attributes)
rescue
# Should I perhaps add some line here that adds the users errors to the memory?
raise ActiveRecord::Rollback
end
end
end
if #organization.persisted?
flash[:success] = "Yeah!"
redirect_to root_url
else
#user = #organization.users.build(users_attributes) # Otherwise the filled in information for user is gone (fields for user are then empty)
render :new
end
end
The form view includes:
<%= form_for #organization, url: next_url do |f| %>
<%= render partial: 'shared/error_messages', locals: { object: f.object, nested_models: f.object.users } %>
<%= f.text_field :name %>
# Other fields
<%= f.fields_for :users do |p| %>
<%= p.email_field :email %>
# Other fields
<% end %>
<%= f.submit "Submit" %>
<% end %>
The error messages partial is as follows:
<% object.errors.full_messages.each do |msg| %>
<li><%= msg.html_safe %></li>
<% end %>
Update: Following the steps from Rob's answer I arrived at the errors partial below. This still does not display error messages for User. I added debugger responses inside the code below and for some reason nested_model.errors.any? returns false, while the debugger inside the controller (see above) does return error messages for user.
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg.html_safe %></li>
<% end %>
</ul>
</div>
<% end %>
<% if defined?(nested_models) && nested_models.any? %>
# Debugger: responds with "local-variable" for "defined?(nested_models)" and for "nested_models.any?" returns true.
<div id="error_explanation">
<ul>
<% nested_models.each do |nested_model| %>
# Debugger: "nested_model" has the same values as "nested_models.any?", as you would expect. But for "nested_model.errors.any?" it returns false, which it shouldn't.
<% if nested_model.errors.any? %> #Initially had "unless nested_model.valid?" but then errors for User are immediately displayed on loading the form page (new method).
<ul>
<% nested_model.errors.full_messages.each do |msg| %>
<li><%= msg.html_safe %></li>
<% end %>
</ul>
<% end %>
<% end %>
</ul>
</div>
<% end %>
Try adding validates_associated :users under your has_many :users association in Organization.
http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validates_associated
Did you code successfully create a person during the rescue block?
rescue ActiveRecord::RecordInvalid => exception
# do something with exception here
raise ActiveRecord::Rollback
#organization.users.build if #organization.users.blank?
render :new and return
This code looks like it will create a new empty User regardless of incorrect validations. And render new will simply return no errors because the user was successfully created, assuming Organization has no Users.
The control flow of this method has a few outcomes, definitely needs to be broken down some more. I would use byebug and walk through the block with an incorrect Organization, then incorrect name. Then an empty Organization with incorrect User attributes.
organization has_many :users and user belongs_to :organization
organization.rb
accepts_nested_attributes_for :users
new.html.erb
<%= form_for #organization, url: next_url do |f| %>
<%= render 'shared/error_messages', object: #organization %>
<%= f.text_field :name %>
# Other fields
<%= f.fields_for(:users,#organization.users.build) do |p| %>
<%= p.email_field :email %>
# Other fields
<% end %>
<%= f.submit "Submit" %>
<% end %>
In controller
def create
#organization = Organization.new(new_params)
if #organization.save
flash[:success] = "Yeah!"
redirect_to root_url
else
render :new
end
end
This is very related to this question. The key is that <%= render 'shared/error_messages', object: f.object %> is, I assume, only calling the .errors method on the object it is passed (in this case, organization).
However, because the user errors reside with the user object, they won't be returned and therefore will not be displayed. This requires simply changing the view logic to also display the results of .errors on the various user models. How you want to do so is up to you. In the linked thread, the accepted answer had the error message display code inline instead of in a partial, so you could do it that way, but it would be somewhat redundant.
I would modify my shared/error_messages.html.erb file to check for another passed local called something like nested_models. Then it would use that to search the associated models and include the errors on that. We just would need to check whether it is defined first so that your other views that don't have a nested model won't cause it to raise an error.
shared/error_messages.html.erb
<% if object.errors.any? %>
<div class="error-messages">
Object Errors:
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% if defined?(nested_models) && nested_models.any? %>
Nested Model(s) Errors:
<ul>
<% nested_models.each do |nested_model| %>
<% unless nested_model.valid? %>
<li>
<ul>
<% nested_model.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</li>
<% end %>
<% end %>
</ul>
<% end %>
</div>
<% end %>
Then you would just need to change a single line in your view:
<%= render partial: 'shared/error_messages', locals: { object: #organization, nested_models: #organization.users } %>
Looks like you have a lot of untestable logic in your controller. Looks like for you logic will be better to use simple FormObject pattern.
https://robots.thoughtbot.com/activemodel-form-objects
I created a sample webpage in rubyonrails which has two textbox and a button . When i enter some data in the text box and click the button no error appears . But the data is not stored in the data base . What is the mistake that i committed .
login.html.erb file :
<%= form_for #product, url:{action: "login"} do |f| %>
<% if #product.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#product.errors.count, "error") %> prohibited this product from being saved:</h2>
<ul>
<% #product.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :username %><br>
<%= f.text_field :username %>
</div>
<div class="field">
<%= f.label :password %><br>
<%= f.text_field :password %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
routes.rb file:
Sample::Application.routes.draw do
get "sample/login"
root 'sample#login'
post 'sample/:id' => 'sample#login'
end
sample controller file :
class SampleController < ApplicationController
def login
#product=Logz.new
end
end
and the model class name Logz contains the necessary field names
username and password . If there was any error i could manage . but it shows no errors.
I had the same problem once. I dont know how its happened . but i just changed the content in database.yml
localhost:yourdb_development
into
127.0.0.1:ypurdb_development
and i got it working.
You are sending the data to method login, but it just instantiate a new Product and it aren't receiving any attributes... and even if received... it are not saving the Product at all, so does not persist the data.
Try add a method create... that will be responsible for receive the data and save
class SampleController < ApplicationController
def create
#product = Logz.new(params[:product])
if #product.save
format.html { redirect_to 'sample#login', notice: 'Data saved successfully' }
else
flash[:notice] = 'A wild error appeared'
end
end
After that, create the route to post 'sample/create' and change the action to where your form send the data... form_for #product, action: "create", method: 'post'
Doing that... i will be possible to persist the data on your database...
PS:
You can use the content of that method inside your login method... but I dont recommend that... it is ugly and does not follos the conventions of rails.
I even recommend you to do a refactory... because it doesn't make sense access a SampleController in order to create a Product... that is persisted in an object called Logz...
The best practice is all follow the same name... LogzController, #logz, and finally your model Logz. and preferably your routes following the same pattern...
Another thing is, it would be nice to change your method login to a method call 'new' because that method you use to fill a new Logz... not to login...
I'm working on a reddit mock-up through a tutorial. When on my localhost I am on my new page(submit new link) where I can submit a title and url.
Whenever I submitted the information, I would previously end up on a blank create view.
The tutorial is asking for us to find a way to populate our database and end up on a show view with our submitted information & also have our newly submitted information be available on our index page.
This is my attempt at editing my controller for this, but I've failed miserably:
class LinksController < ApplicationController
def index
#link = Link.all
end
def show
#link = Link.find(params[:id])
end
def new
#link = Link.new
end
def create
#link = Link.new(link_params)
if #link.save
redirect_to #link
else
render action: 'new'
end
end
end
I am getting an error that reads:
undefined local variable or method `link_params'
Any advice on how to fix this?
This is my new view:
This is new view:
<%= form_for(#link) do |f| %>
<% if #link.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#link.errors.count, "error") %> prohibited this link from being saved:</h2>
<ul>
<% #link.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :url %><br />
<%= f.text_field :url %>
</div>
<div class="field">
<%= f.label :title %><br />
<%= f.text_field :title %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
My show view is currently empty save for some header text:
Also, if someone had some advice on how to go about further improving my controller & views as well to get the desired result that would be appreciated, I am new & trying to learn, thanks.
You need a link_params method in your controller
def link_params
params.require(:link).permit(:url, :title)
end
When you submit a form from browser, it hits the controller action. The form will submit some parameters which needs to be processed. Follow rails logs to see what is being returned, this is the best simple way to know and debug things.
Here in your create action, #link = Link.new(link_params) you are initializing Link object with link_params data, when the form is submitted check what parameter contains the hash. Parse over that hash to pass data in Link class object. You need to define link_params or use something like params[:link] after confirming from the server logs.
I currently have a simple app that includes user authentication through devise and a message model(the message model uses Jquery and Faye). Both are working fine independently, but I would like to add a user_id to the messages.
I have already updated the schema and models with the relationship, but I am having trouble inputting the necessary code to have the view and controller input the relationship into the db, while keeping the jquery working. I wasn't sure of the best way, but here I tried to create a hidden field that would pull the user_id, not sure if this is even possible. Here is the applicable code, any help is appreciated.
Message index.html.erb
<ul id="chat">
<%= render #messages %>
</ul>
<%= form_for Message.new, :remote => true do |f| %>
<div class="field">
<%= f.text_field :content %>
</div>
<div class="field">
<%= f.hidden_field :user_id %>
</div>
<div class="actions">
<%= f.submit "Send" %>
</div>
<% end %>
create.js.erb for messages
<% broadcast "/messages" do %>
$("#chat").append("<%= escape_javascript render(#user.message) %>");
<% end %>
$("#new_message")[0].reset();
Messages Controller
class MessagesController < ApplicationController
def index
if #authentications = current_user
#messages = Message.all
else
redirect_to authentications_url
flash[:notice] = "You need to sign in before answering questions"
end
end
def create
#user = User.find(params[:user_id])
#message = #user.message.create(params[:message])
end
end
I think you have everything you would need, but if not, let me know and I will be happy to provide it for you.
Thanks, everyone.
two things to correct,
1)use user association to create message instance in form(probably current_user if logged-in user create a message)
<%= form_for user.messages.new, :remote => true do |f| %> #// assuming its has many association
2) if it is has_many association then change association in create action
#message = #user.messages.create(params[:message])