I have home controller and trying to update the field of a table of different controller.
Home controller
class HomeController < ApplicationController
before_action :user_params, only: [:index]
def index
#email = Email.new(user_params)
end
def contact
end
def faq
end
def team
end
def privacy
end
def esave
if !user_params.nil?
if #email.save_with_captcha
flash[:notice] = "Thank you for registering you email address"
redirect_to :action => 'index'
else
render 'esave'
end
end
end
def user_params
params.require(:email).permit(:email, :captcha, :captcha_key) if params[:email]
end
end
So I am trying to create new field of Email Model in my home controller but when I click save it throws this error...
App 24078 stderr: Completed 500 Internal Server Error in 3ms
App 24078 stderr:
App 24078 stderr: NoMethodError (undefined method `save_with_captcha' for nil:NilClass):
App 24078 stderr: app/controllers/home_controller.rb:37:in `esave'
I have email model with defined validations which is like this
class Email < ActiveRecord::Base
apply_simple_captcha :message => "The secret Image and code were different", :add_to_base => true
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/ , :message => "Invalid Format"
end
So I do not understnd why this save_simple_captcha is nil, any suggestions
Here my 2 cents.
Remember HTTP is stateless. You have defined #email in the index, you have gotten to the esave after page has been refreshed so #email is no longer available.
( Controller is a bit different that straight Ruby Class that get instantiated and variables can be tossed around )
You HAVE TO instantiate the #email for the method your are in.
I don't know what is in your Model so I can just guess the rest.
This #email = Email.find(params[:id]) might or might not work as I don't know your model. ( as mentioned in other comments )
Put your model here too. We might be able to help you out.
PS: It is not a good idea to accept to code on a framework when you don't know a very basic of it :)
Related
While registration, I want to check whether the given email by a new user already exists or not in my controller.
class LoginsController < ApplicationController
skip_before_action :verify_authenticity_token
def index
#subscriber = Subscriber.new()
end
def sign_up
subscriberNew = Subscriber.new
subscriberNew.name = params[:name]
subscriberNew.cus_user_name = params[:user_name]
subscriberNew.cus_password = params[:password]
subscriberNew.cus_email = params[:email]
subscriberNew.mobile_no = params[:phone]
#if Email exists sends and error message
#...................
#if email does not exist, save the response to database
result = subscriberNew.save
respond_to do |format|
msg = {:status => "ok", :message => "Success!"}
format.json {render :json => msg}
end
end
end
How can I do this?
There are multiple ways to validate unique records, one of the better approaches may be altering your database to set a unique index for the email:
add_index :users, :username, unique: true (in your migration)
The DB index approach is better in long terms performance (see this for example)
You can also validate it in your controller before_action:
before_action :validate_email, only: [:sign_up]
...
private
def validate_email
# Or whatever way of sending a message you prefer
flash[:notice] = "A user with this email already exists"
redirect_to root_path if User.where(email: params[:email]).exists?
end
I'd recommend further reading about Active Record validations in the Rails Guides.
Add a validation for the email with uniqueness: true https://guides.rubyonrails.org/active_record_validations.html#uniqueness
You can do something like:
class Subscriber < ApplicationRecord
validates :email, uniqueness: true
end
and on the action:
subscriberNew.valid?
if subscriberNew.errors[:email].present?
#show_error
else
#success
end
I'd really recommend you to read about rails naming conventions, validations using activerecord and also conventions when creating a form (with form_for helper) and Strong Parameters https://guides.rubyonrails.org/action_controller_overview.html#strong-parameters.
before I ask the question I want to give a little background on the models. I have a user_conversation model(through table) which accepts attributes from conversations and messages models. The create action and before action are given below.
before_action :logged_in_user
before_action :validate_conversation, only: :create
def create
redirect_to home_path unless current_user
#conversation = UserConversation.new conversation_params
#conversation.user = current_user
#conversation.conversation.messages.first.user = current_user
#conversation.save!
activate_unread
redirect_to user_conversation_path(current_user,#conversation)
end
Private
def validate_conversation
#user = User.find params[:user_id]
if params[:user_conversation][:conversation_attributes]["subject"].blank?
redirect_to new_user_conversation_path(#user)
flash[:danger] = "Subject cannot be blank"
else params[:user_conversation][:conversation_attributes][:messages_attributes]["0"]["body"].blank?
redirect_to new_user_conversation_path(#user)
flash[:danger] = "Message cannot be blank"
end
end
def conversation_params
params.require(:user_conversation).permit(:recipient_id, conversation_attributes: [:subject, messages_attributes: [:body]])
end
I was trying to write an integration tests for the post request of user_conversation. The test is given below.
require 'test_helper'
class ConversationCreateTest < ActionDispatch::IntegrationTest
def setup
#user = users(:user_a)
#conversation = conversations(:convo_one)
end
test "invalid creation of a user conversation no subject" do
log_in_as(#user)
get new_user_conversation_path(#user)
post user_conversations_path(#user), user_conversation: {:recipient_id => #user.id, :conversation_attributes => {:subject => "this is a subject",
:message_attributes => {"0" => {:body => "sending a message"}}}}
end
I get the following error message when I run the command.
1) Error:
ConversationCreateTest#test_invalid_creation_of_a_user_conversation_no_subject:
NoMethodError: undefined method `[]' for nil:NilClass
app/controllers/user_conversations_controller.rb:63:in `validate_conversation'
test/integration/conversation_create_test.rb:13:in `block in <class:ConversationCreateTest>'
191 runs, 398 assertions, 0 failures, 1 errors, 0 skips
I have been trying to debug the problem for about 2 hours. I have checked the test log files and it says internal server error 500. I have tried commenting certain lines of codes to narrow down the problem but not really sure what the problem is. Any help would be appreciated.
In rails, validations are made with the ActiveModel::Validators.
So you can simply validate your model like this:
User:
class User
has_many :conversations, through: "user_conversations"
end
Conversation:
class Conversation
has_many :users, through: "user_conversations"
validates_presence_of :subject, :messages
end
See more here about validations
So if you then need to validate your model you can call:
conversation = Conversation.create(subject: nil)
conversation.errors.full_messages # => ["Subject can't be blank"]
I think you'll need to rewrite a bunch of things in your app, and if you took the code above you can simply test this thing within a model (unit) test.
Which, by the way, is no longer needed because you don't want to test the rails provided validators. You probably just want to test your own validators.
I am using stripe to setup a payment system. I am having an issue with my credit_card model displaying the stripe errors in my user model, as the first credit card is submitted as a child of the user. Also, I am using Devise for my users auth.
class CreditCard < ActiveRecord::Base
def create_stripe_credit_card
customer = Stripe::Customer.retrieve(self.user.stripe_customer_id)
credit_card = customer.cards.create(:card => self.stripe_token)
rescue Stripe::CardError => e
logger.error "Stripe Error: " + e.message
errors.add(:base, "#{e.message}")
false
end
end
class RegistrationsController < Devise::RegistrationsController
def create
build_resource(sign_up_params)
if resource.save
# User created
else
clean_up_passwords resource
return render :status => 400, :json => resource.errors
end
end
end
After some googling I came upon this SO question
Ruby on Rails: how to get error messages from a child resource displayed?
but it seems that I would have to put a validator in my CreditCard model to trigger the errors in my User model, and since the only way to get this error raised is to try it, not sure how to put it into a validator.
Any help would be greatly appreciated.
I am trying to pass a hidden field from a form whose value is derived from a text blob that user can edit on the webpage. (I use bootstrap-editable to let the user edit the blurb by clicking on it)
Here is the actual workflow:
User goes on 'Invitations page' where they are are provided with a form to enter friends email and shown a default text that will be used in the email
If the user want they can click on the text and edit it. This will make a post call via javascript to update_email method in Invitation controller
After the text is updated user is redirected back so now the user sees the same page with updated text. This works and user sees the updated text blurb instead of default [1-3] can happen any number of times
When the user submits the form , I expect to get the final version of email that I can save in the db and also trigger an email invitation to the users friend
Problem:
I keep getting default text from form parameters. Any idea what I am doing wrong?
Here is the form (Its haml instead of html)
#new-form
= form_for #invitation, :url=> invitations_path(), :html => {:class => 'form-inline', :role => 'form'} do |f|
.form-group
= f.text_field :email, :type=> 'email', :placeholder=> 'Invite your friends via email', :class=> 'form-control invitation-email'
= f.hidden_field :mail_text, :value => #invitation_email
= f.submit :class => 'btn btn-primary submit-email', :value => 'Send'
Here is the invitation controller:
class InvitationsController < ApplicationController
authorize_resource
before_filter :load_invitations, only: [:new, :index]
before_filter :new_invitation, only: [:new, :index]
before_filter :default_email, only: [:index]
#helper_method :default_email
def create
Invitation.create!(email: params[:invitation][:email], invited_by: current_user.id, state: 'sent', mail_text: params[:invitation][:mail_text], url: {referrer_name: current_user.name}.to_param)
redirect_to :back
end
def update_email
#invitation_email = params[:value]
flash[:updated_invitation_email] = params[:value]
redirect_to :back
end
private
def invitation_params
params.require(:invitation).permit!
end
def load_invitations
#invitations ||= current_user.sent_invitations
end
def new_invitation
#invitation = Invitation.new
end
def default_email
default_text = "default text"
#invitation_email = flash[:updated_invitation_email].blank? ? default_text : flash[:updated_invitation_email]
end
end
Assuming you are using Rails 4 then you need to permit the mail_text parameter:
class InvitationsController < ApplicationController
# ...
private
def invitation_params
params.require(:invitation).permit(:email, :mail_text) #...
end
end
Depending on your settings rails strong parameters will either raise an error or just silently null un-permitted params.
I have to say that your flow is a bit weird and that it may be better if you actually use a
more RESTful pattern:
1. User goes on 'Invitations page' where they are are provided with a form to enter friends email and shown a default text that will be used in the email
Send a AJAX POST request to /invitations (InvitationsController#create) it should return a JSON representation of the UNSENT invitation, store the returned invitation id on the form.
Note that you may need to setup the validations on your Invitation model so that it allows :email and :mail_text to be blank on creation
class Invitation < ActiveRecord::Base
validates :email, allow_blank: true
# ...
# Do full validation only when mail is being sent.
with_options if: :is_being_sent? do |invitation|
invitation.validates :email #...
invitation.validates :mail_text #...
end
# ...
def is_being_sent?
changed.include?("state") && state == 'sent'
end
end
2. User edits text
Send a AJAX PUT or PATCH request to /invitations/:id and update the invitation.
3. User clicks send
Send a POST request to /invitations/:id/send. Update the state attribute and validate.
If valid send invitation. Display a message to user.
class InvitationsController < ApplicationController
# ...
# POST /invitations/:id/send
def send
#invitation = Invitation.find(params[:id])
# Ensure we have latest values from form and trigger a more stringent validation
#invitation.update(params.merge({ state: :sent })
if #invitation.valid?
#mail = Invitation.send!
if #mail.delivered?
# display success response
else
# display error
end
else # record is invalid
# redirect to edit
end
end
# ...
end
I am building a simple web app that sends SMS messages to cell phones using Twilio. I want to ensure that the user has entered a full 10 digit phone number before it will allow a message to attempt to be sent.
When I test it with a less-than or greater-than 10 digit number, in heroku logs, I see Twilio::REST::RequestError (The 'To' number 1234567890 is not a valid phone number.).
I have tried to use a begin/rescue wrapper and am telling it to render text: "Try again with a valid number." and tried a variety of if statements to try to avoid the error.
I am pretty new to Ruby and Rails and Twilio, but I promise i have been through every guide I have found. Any help is greatly appreciated. Full code of my UserController below:
require 'twilio-ruby'
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
account_sid = '...'
auth_token = '...'
if #user.save
render text: "Wasn't that fun? Hit the back button in your browser to give it another go!"
begin
client = Twilio::REST::Client.new account_sid, auth_token
client.account.sms.messages.create(
from: '+16035093259',
to: #user.phone,
body: #user.message
)
rescue Twilio::REST::RequestError
render text: "Try again with a valid number."
end
else
render :new
end
end
end
I'd extract the SMS sending logic into a separate model/controller and use a background job to process the submitting. The UserController should only handle, well, user creation/modification.
Scaffolding:
$ rails g model sms_job user:references message:text phone submitted_at:datetime
$ rake db:migrate
Model:
class SmsJob < AR::Base
attr_accessible :user_id, :message, :phone
belongs_to :user
validates_presence_of :message, :phone, :user_id
validates :phone,
length: { min: 10 },
format: { with: /\+?\d+/ }
scope :unsubmitted, where(submitted_at: nil)
TWILIO = {
from_no: '...',
account_sid: '...',
auth_token: '...'
}
# find a way to call this method separately from user request
def self.process!
unsubmitted.find_each do |job|
begin
client = Twilio::REST::Client.new TWILIO[:account_sid], TWILIO[:auth_token]
client.account.sms.messages.create(
from: TWILIO[:from_no],
to: job.phone,
body: job.message
)
job.submitted_at = Time.zone.now
job.save
rescue Twilio::REST::RequestError
# maybe set update a tries counter
# or delete job record
# or just ignore this error
end
end
end
end
The controller then should just provide the information that the SMS is going to be send:
# don't forget the 'resources :sms_jobs' in your routes.rb
class SmsJobsController < ApplicationController
# index, update, destroy only for only admin?
def new
#sms_job = SmsJobs.new
end
def create
#sms_job = current_user.sms_jobs.build params[:sms_job]
if #sms_job.save
redirect_to root_url, notice: "Your message is being send!"
else
render :new
end
end
end
For the background processing, have a look at these excellent Railscasts :-) You probably need to workaround some concurrency problems if you have to process many messages and/or Twilio has a long response time (didn't use that service yet).