Retrieve multiple records with find_by method - ruby-on-rails

The method below works and authenticates a user who has been sent a token-link by email.
def login
inv = Invitation.find_by(email: params[:email])
if inv && inv.authenticated?(:invitation, params[:id])
#organization = inv.organization
unless #organization.nil?
render 'profiles/show' and return
else
flash[:danger] = "Error"
redirect_to root_path
end
else
flash[:danger] = "Invalid link"
redirect_to root_path
end
end
This method however seems to assume a person (inv) can only exist once in the Invitation database. This is not the case, the same person/email address can have multiple records in the database. Therefore, I need to rewrite the method to account for such a situation. How can I do this? Can I use .all as added on line 2 below, and use .each?
def login
inv = Invitation.find_by(email: params[:email]).all
if inv
inv.each do |person|
if person.authenticated?(:invitation, params[:id])
#organization = person.organization
unless #organization.nil?
render 'profiles/show' and return
else
flash[:danger] = "Error"
redirect_to root_path and return
end
end
flash[:danger] = "Invalid link"
redirect_to root_path
end
else
flash[:danger] = "Invalid link"
redirect_to root_path
end
end
Error messages:
This code produces the error message below but I'm not sure what else than .all to use:
NoMethodError: undefined method `all' for #<Invitation:0x0000000b869ee8>

You need to use find_all_by or where
inv = Invitation.find_all_by(email: params[:email])
or
inv = Invitation.where(email: params[:email])

Related

Rails: flash notice unless only certain attributes updated

I want to show a flash notice in my Rails app all the time unless only certain attributes are updated.
def update
if #user.update_attributes(user_params)
if user_params == [:user][:certain_params]
redirect_to users_path
else
redirect_to users_path, notice: "#{#user.name} saved."
end
else
redirect_to edit_user_path(#user), flash: { error: #user.mapped_errors }
end
end
Something like this pseudocode?
Use the changed method to get an array of the attributes that are changed, create a flash message if the attributes are not the "non-notify" attributes.
def update
#user.assign_attributes(user_params)
changed_fields = #user.changed
if #user.save
flash[:notice] = "#{#user.name} saved." if changed_fields != ["photo"]
redirect_to users_path
else
redirect_to edit_user_path(#user), flash: { error: #user.mapped_errors }
end
end
This will show the saved notice if they change photo plus other attributes, or if they change other attributes but not photo. The message is suppressed only if only photo is changed.
def update
#user = User.find(params[:id])
# lets say you have these attributes to be checked
attrs = ["street1", "street2", "city", "state", "zipcode"]
attributes_changed = (#user.changed & attrs).any?
if #user.update_attributes(user_params)
flash[:notice] = "#{#user.name} saved." if attributes_changed
redirect_to users_path
else
redirect_to edit_user_path(#user), flash: { error: #user.mapped_errors }
end
end
For more info see
Rails 3 check if attribute changed
http://api.rubyonrails.org/classes/ActiveModel/Dirty.html

What do I put in my sessions controller in order to allow two models to login?

So I am having trouble in my sessions controller. I am trying to allow users to signin and businessusers to signin. They both have their own respective model and table in the database. However only users can signin. This is my sessions controller currently. What can I add to the controller to allow businessusers to also signin?
class SessionsController < ApplicationController
def new
end
def create
customer = Customer.find_by_name(params[:name])
if customer && customer.authenticate(params[:password])
session[:customer_id] = customer.id
redirect_to customer
else
flash.now[:notice] = "Invalid name/password combination."
render 'new'
end
end
def destroy
if signed_in?
session[:customer_id] = nil
else
flash[:notice] = "You need to sign in first"
end
redirect_to signin_path
end
end
class SessionsController < ApplicationController
def new
end
def create
customer = Customer.find_by_name(params[:name])
if customer.present? && customer.authenticate(params[:password])
session[:user_type] = 'Customer'
session[:customer_id] = customer.id
redirect_to customer
else
businessuser = BusinessUser.find_by_name(params[:name])
if businessuser.present? && businessuser.authenticate(params[:password])
session[:user_type] = 'Business'
session[:businessuser_id] = businessuser.id
redirect_to businessuser
else
flash.now[:notice] = "Invalid name/password combination."
render 'new'
end
end
end
def destroy
if signed_in?
session[:user_type] = nil
session[:customer_id] = nil
session[:businessuser_id] = nil
else
flash[:notice] = "You need to sign in first"
end
redirect_to signin_path
end
end

Resque job undefined method 'current_user'

It's my first attempt at Resque and doing background jobs, and I'm kind of stuck.
I have two issues at hand.
It gives me the error
undefined local variable or method `current_user'
I am not sure if what I am pushing to the worker is indeed the most correct thing.
Here is my code:
schedules_controller.rb
def trial
#schedule = current_user.schedules.new
if #schedule.save
user = User.find(params[:id])
#client = Buffer::Client.new(user.token)
Resque.enqueue(ScheduleTweets, #client)
#schedule.update_attribute(:trial, true)
flash[:notice] = "success"
redirect_to :back
else
flash[:alert] = "Try again."
redirect_to :back
end
end
and the worker:
apps/workers/schedule_tweets.rb
class ScheduleTweets
#queue = :schedules_queue
def self.perform(client)
user = User.find(params[:id])
client = Buffer::Client.new(user.token)
#list = List.first(6)
#profiles = client.profiles
#profile_ids = profiles.map(&:id)
#list.each do |list|
client.create_update(body: {text: "#{list.text}", profile_ids: #profile_ids })
end
end
end
end
my thought process is that the client is the core of the entire process and should thus be the one. However #profile_ids also contains anywhere from 1-5 values.
When I run the task I get the undefined local variable or method 'current_user'. How do I fix that, also am I doing it right by choosing the #client as the thing to add?
Try this
schedules_controller.rb
def trial
#schedule = current_user.schedules.new
if #schedule.save
user = User.find(params[:id])
#client = Buffer::Client.new(user.token)
Resque.enqueue(ScheduleTweets, user.id)
#schedule.update_attribute(:trial, true)
flash[:notice] = "success"
redirect_to :back
else
flash[:alert] = "Try again."
redirect_to :back
end
end
and the worker:
apps/workers/schedule_tweets.rb
class ScheduleTweets
#queue = :schedules_queue
def self.perform(user_id)
user = User.find(user_id)
client = Buffer::Client.new(user.token)
#list = List.first(6)
#profiles = client.profiles
#profile_ids = #profiles.map(&:id)
#list.each do |list|
client.create_update(body: {text: "#{list.text}", profile_ids: #profile_ids })
end
end
end

Rails: Begin Rescue block not being called in private method

In my rails app I have this bit of logic in my SessionsController's create method that I want to refactor out by extracting it into a method:
if login_form.validate(params[:user])
begin
#user = User.find_by!(email: params[:user][:email])
rescue ActiveRecord::RecordNotFound => e
flash.now.alert = 'invalid user credentials'
render :new and return
end
else
flash.now.alert = login_form.errors.full_messages
render :new and return
end
All this does is check if user exists in the system. I want to refactor this into
if login_form.validate(params[:user])
find_user
else
flash.now.alert = login_form.errors.full_messages
render :new and return
end
And then the private find_user method:
private
def find_user
begin
#user = User.find_by!(email: params[:user][:email])
rescue ActiveRecord::RecordNotFound => e
flash.now.alert = 'invalid user credentials'
render :new and return
end
end
But now after I do this, the ActiveRecord::RecordNotFound exception is ignored! The method does not stop at the return command. Why is that? What am I doing wrong?
UPDATE:
I understand what I am doing wrong. But how do I do this in the right way?
The return statement just returns from the function where it is called, that is the find_user method.
Ok so I can use a block like so:
if login_form.validate(params[:user])
find_user do
flash.now.alert = 'invalid user credentials'
render :new and return
end
else
flash.now.alert = login_form.errors.full_messages
render :new and return
end
And then refactor the private method like so:
def find_user(&block)
begin
#user = User.find_by!(email: params[:user][:email])
rescue ActiveRecord::RecordNotFound => e
yield
end
end

Ruby on Rails: redirect_to not working after create and save

I want to redirect_to slider_path after a user submits their email. Currently, only the success message is displayed without a redirect. Here's the code:
class Splash::SubscribersController < ApplicationController
def create
#subscriber = Subscriber.new(params[:subscriber])
if #subscriber.save
success = true
message = "Success! We'll let you know when we launch."
else
success = false
message = "Fail."
end
respond_to do |format|
format.html {
if success
flash[:success] = message
redirect_to slider_path
else
flash[:error] = message
end
redirect_to root_path
}
format.json { render :json => { :success => success, :message => message }.to_json }
end
end
end
Just replace this part of your code:
if success
flash[:success] = message
redirect_to slider_path
else
flash[:error] = message
end
redirect_to root_path
with this:
if success
flash[:success] = message
redirect_to slider_path
else
flash[:error] = message
redirect_to root_path
end
Rails API states:
An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
def do_something
redirect_to :action => "elsewhere"
render :action => "overthere" # raises DoubleRenderError
end
If you need to redirect on the condition of something, then be sure to add “and return” to halt execution.
def do_something
redirect_to(:action => "elsewhere") and return if monkeys.nil?
render :action => "overthere" # won't be called if monkeys is nil
end
Note the use of and return
Neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".
Add a return statement after your redirect. If the action also renders a template by default, any redirects need to be followed by a return statement.
if success
flash[:success] = message
redirect_to slider_path
return # <= Add a return.
else
flash[:error] = message
end
redirect_to root_path

Resources