Trying to use current_user where it is undefined - ruby-on-rails

I have this call in my vote model:
fires :vote_updated, :on => :update,
:actor => :user,
:secondary_subject => :video,
:if => lambda { |vote| ((vote.value == 1) || (vote.value == -1)) && (vote.video.user != current_user)}
In case you aren't familiar, it works with the timeline_fu plugin.
I do not want the call to be fired if the user who owns the voted up video is the current user. That is where this line comes in:
:if => lambda { |vote| ((vote.value == 1) || (vote.value == -1)) && (vote.video.user != current_user)}
However, I do not have access to current_user here. How do I get around this?
Here's the create method in my votes controller (there actually is no update method):
def create
#video = Video.find(params[:video_id])
#vote = current_user.video_votes.find_or_create_by_video_id(#video.id)
if #vote.value.nil?
if params[:type] == "up"
#vote.value = 1
else
#vote.value = -1
end
elsif (params[:type] == "up" && #vote.value == 1) || (params[:type] == "down" && #vote.value == -1)
#vote.value = 0
elsif ((params[:type] == "up" && #vote.value == -1) || (params[:type] == "down" && #vote.value == 1)) || (#vote.value == 0)
if params[:type] == "up"
#vote.value = 1
else
#vote.value = -1
end
end
if #vote.save
respond_to do |format|
format.html { redirect_to #video }
format.js
end
else
respond_to do |format|
format.html
format.js
end
end
end

I believe the right thing to do would be validating this in controller. I would create a before filter for this case
UPDATE:
Just as a quick example:
before_filter :valid_vote, :only => :update
def update
#vote.update_attributes(params[:vote]) # or whatever
end
..
private
def valid_vote
#vote = Vote.find params[:id]
unless ( #vote.video.user.id != current_user.id )
render :text => 'You can't vote for your own video', :status => 403
end
end
So #vote is being declared and validated before your 'update' action is proccessed.
If it's not valid then your 'update' action stays untouched
UPDATE 2 :
not sure how you'll like it, but you could also do as follows:
in your Vote model:
attr_accessor :skip_timeline
then use the concept with before filter, but do #vote.skip_timeline = true instead of rendering text
then the statement might look as follows:
:if => lambda { |vote| ((vote.value == 1) || (vote.value == -1)) && !vote.skip_timeline }
You could also move ((vote.value == 1) || (vote.value == -1)) to your before filter :
def valid_vote
#vote = Vote.find params[:id]
unless ( [1,-1].include? #vote.value && #vote.video.user.id != current_user.id )
#vote.skip_timeline = true
end
end
and
:if => lambda { |vote| !vote.skip_timeline }

You are getting this error because it's typically not recommended to access current_user (or session information) in your model. I am not all that familiar with the timeline_fu gem, so this answer isn't going to be the greatest answer you may get. I'm merely going to show you how to access current_user from any model.
First go to your application controller. You'll want to make a method that sets the current user. You need to call the method in the before filter.
before_filter :loadCurrentUser
def loadCurrentUser
  User.currentUser = current_user
end
Then in your User model, you need to define 'currentUser'.
def self.currentUser
Thread.currentUser[:user]
end
You don't necessarily have to declare the current_user in the application controller, but since it's a gem, I'm not sure if it has an easily accessible controller.
Edit: This way may be prone to problems, but I'm not entirely sure if you were asking how to make current_user available in models, or a completely different workaround so you do not have that problem... and reading the responses of the other answer, I'm thinking it's not what you were asking.

Related

how do I use this parameter correctly?

I have a method in my rails app.
def current_user_can_edit?(model)
user_signed_in? && (
model.user == current_user ||
(model.try(:post).present? && model.post.user == current_user)
)
end
The method is needed to check the possibility of editing the model. There are posts and events in my app. I would like to dynamically substitute a post or event for a choice, instead of a static post.
How can I write a method correctly so as not to make a lot of conditions? Like, for example, here:
def current_user_can_edit?(model, action)
if action.class.to_s == 'Post':
user_signed_in? && (
model.user == current_user ||
(model.try(:post).present? && model.post.user == current_user)
)
elsif action.class.to_s == 'Event':
user_signed_in? && (
model.user == current_user ||
(model.try(:event).present? && model.event.user == current_user)
)
end
end
In your method's logic, the expression:
model.try(:post).present? && model.post.user == current_user
can be combined by chaining two try calls:
model.try(:post).try(:user) == current_user
Now you can replace :post by a calculated value:
model.try(action.class.to_s.downcase).try(:user) == current_user
Whole code:
def current_user_can_edit?(model, action)
user_signed_in? && (
model.user == current_user ||
model.try(action.class.to_s.downcase).try(:user) == current_user
)
end
You could also use some guard clauses:
def current_user_can_edit?(model, action)
return unless user_signed_in?
return true if model.user == current_user
model.try(action.class.to_s.downcase).try(:user) == current_user
end
I'm assuming that action.class.to_s returns "Post". The code would of course be easier if you would pass :post as the method's second parameter.

Using 'if', '&&', and '||' in one condition statement

So what I am trying to accomplish is if #provider.licenses.where(issuing_state:'CA')' it has to match the #form.state as well which would be 'CA' in this case in order to have access to that page. Now if the provider.licenses.issuing_state does not match #form.state the user would be redirect. Here is what I have
def edit
#user = current_user
#patient = Patient.find(params[:patient_id])
#form_response = FormResponse.find_by_id(params[:id])
#form = #form_response.form
#provider = current_user.try(:provider)
#provider_user = #provider.try(:role).try(:user)
if current_user.is_provider?
if #provider.licenses.where(issuing_state:'CO') && #form.state == 'CA' || #provider.licenses.where(issuing_state:'CA') && #form.state == 'CO'
redirect_to root_path, alert: 'You do not have access to update form'
end
end
end
Something like #provider.licenses.where(..) is always trueish because it returns a ActiveRecord::Relation.
You might want to use #provider.licenses.exist?(issuing_state:'CO') instead.
if #provider.licenses.exist?(issuing_state:'CO') && #form.state == 'CA' ||
#provider.licenses.exist?(issuing_state:'CA') && #form.state == 'CO'
redirect_to root_path, alert: 'You do not have access to update form'
end

DRYing up similar code in controller methods

I've got two methods in a controller with very similar code. Wondering how I could DRY them up! They both utilize csv-importer gem to parse a csv file.
sales_controller.rb
def import_csv_test
user_id = params[:user_id]
import = ImportSaleCSV.new(file: params[:file]) do
after_build do |sale|
sale.user_id = user_id
skip! if sale.email == nil
skip! if sale.order_date == nil
skip! if sale.amount == nil
end
end
import.run!
redirect_to lifecycle_grid_sales_path, notice: import.report.message
end
def import_ftp
user_id = params[:user_id]
import = ImportSaleCSV.new(path: './public/uploads/gotcha.csv') do
after_build do |sale|
sale.user_id = user_id
skip! if sale.email == nil
skip! if sale.order_date == nil
skip! if sale.amount == nil
end
end
import.run!
redirect_to lifecycle_grid_sales_path, notice: import.report.message
end
Thanks!
I think you can extract a class to do the heavy lifting.
class ImportSaleCSVCreator
def initialize(csv_options = {}, csv_attributes = {})
#csv_options = csv_options
#csv_attributes = csv_attributes
end
def build
ImportSaleCSV.new(csv_options) do
after_build do |sale|
csv_attributes.each { |k, v| sale.public_send("#{k}=", v) }
skip! if sale.email.nil? || sale.order_date.nil? || sale.amount.nil?
end
end
end
private
attr_reader :csv_options, :csv_attributes
end
class Controller
def import_csv
import = ImportSaleCSVCreator.new({ file: params[:file] }, { user_id: params[:user_id] })
import.run!
end
def import_ftp
import = ImportSaleCSVCreator.new({ path: './gotcha.csv' }, { user_id: params[:user_id] })
import.run!
end
end
Make sure you check attributes passed. Especially when dealing with files, paths, etc. You might want to filter the parameters in ImportSaleCSVCreator.
You may refactor both your methods into single one:
def import(hash)
user_id = params[:user_id]
import = ImportSaleCSV.new(hash) do
after_build do |sale|
sale.user_id = user_id
skip! if sale.email == nil
skip! if sale.order_date == nil
skip! if sale.amount == nil
end
end
import.run!
redirect_to lifecycle_grid_sales_path, notice: import.report.message
end
And then call it:
import({file: params[:file]})
import({path: './public/uploads/gotcha.csv'})
It doesn't seem that method belongs to your controller so you may want to extract it somewhere. I encourage you to check this great article and extract your method into brand new Service object.

Ruby on Rails filter array using three fields

I am trying to search through my model using 3 columns. Also if the column is empty, it is valid. This is how I am doing it
def getactivityfortoday
#temp = params[:temp]
logger.debug "params temp:#{#temp.inspect}"
#sky = params[:sky]
#day = params[:day]
#todaysactivities = []
#activities=[]
#finaldata = []
#activities = Weatherclockactivity.all
#attemptactivities = []
#attemptactivities = #user.attempts
for activity in #activities do
logger.debug "activity: #{activity.attributes.inspect}"
if #temp.to_i < activity.temperatureMax.to_i && #temp.to_i > activity.temperatuureMin.to_i
if #sky == activity.sky || activity.sky == ""
if #day == activity.day
#todaysactivities << activity
end
end
end
end
for activity in #todaysactivities
for attempt in #attemptactivities
if attempt == activity
finaldata << {activity: activity, attempt: "yes"}
else
finaldata << {activity: activity, attempt: "no"}
end
end
end
respond_to do |format|
format.html { render action: "new" }
format.json { render json: #finaldata }
end
The response I get is an empty array but I should be getting 3 rows as a response.
spelling mistake here
activity.temperatuureMin.to_i
And
finaldata << {activity: activity, attempt: "yes"}
should be
#finaldata << {activity: activity, attempt: "yes"}
Also you could be more concise
def getactivityfortoday
#temp = params[:temp]
logger.debug "params temp:#{#temp.inspect}"
#sky = params[:sky]
#day = params[:day]
#activities = Weatherclockactivity.all
#attemptactivities = #user.attempts
#finaldata = #activities.map do |activity|
if (activity.temperatureMin.to_i + 1...activity.temperatureMax.to_i).include?(#temp.to_i) && ( #sky == activity.sky || activity.sky == "") && #day
#attemptactivities.include?(activity) ? {activity: activity, attempt: "yes"} : {activity: activity, attempt: "no"}
end
end.compact
respond_to do |format|
format.html { render action: "new" }
format.json { render json: #finaldata }
end
end
How about something like this?
I tried to make it a balance of readability and conciseness. First we filter for the desired activities. Then we structure the output. This should be easier to debug.
def getactivityfortoday
#temp = params[:temp].to_i
#sky = params[:sky]
#day = params[:day]
#activities = Weatherclockactivity.all
#attemptactivities = #user.attempts
selected_activities = #activities.select do |activity|
# Make sure it's the right temperaure
return false unless (activity.temperatureMin.to_i + 1 ... activity.temperatureMax.to_i).include? #temp
# Make sure the sky matches, or the sky is blank
return false unless (#sky.blank? || #sky.activity == activity.sky)
# Make sure the day matches
return false unless #day == activity.day
# Otherwise, it's good!
return true
end
selected_attempted_activities = selected_activities.map do|activity|
ret = {activity: activity}
ret[:attempt] = #attemptactivities.include?(activity) ? "yes" : "no"
ret
end
respond_to do |format|
format.html { render action: "new" }
format.json { render json: selected_attempted_activities }
end
end
There are a few typos in your original (for instance, #finaldata not finaldata). Make sure that you spell instance variables (things starting with #, like #sky) correctly, since if you try to access an undefined instance variable, it'll silently default to nil.
The best and flexible way is to use ActiveModel::Model
It allows you to use many more useful methods.
it will seems like:
app/models/activity_report.rb
Class ActivityReport
include ActiveModel::Model
attr_accessor :day, :activity # and etc.
validates :day, presence: true
def day
#day.to_s # for example
end
def day=(value)
#day = value - 1.month # for example every date which user set will set on one month ago
end
# and etc
end
app/controllers/posts_controller.rb
...
def index
#activity = ActivityReport.new(params[:activity])
end
def create
#activity.create!
end
...
app/views/posts/index.html.haml
= form_for #activity do |f|
= f.day
For more information you could take a look at:
http://edgeapi.rubyonrails.org/classes/ActiveModel/Model.html
http://railscasts.com/episodes/219-active-model (old)
http://railscasts.com/episodes/416-form-objects (newer, but a little complex)

Redirct_to in application controller caused infinte loop

After detect the browser page will we redirecte.
def detect_browser
redirect_to "privacy" if browser.ie6? || browser.ie7? || browser.firefox?
end
Causes the infinite loops.?
Try something like this:
def detect_browser
if(browser.ie6? || browser.ie7? || browser.firefox? ) &&
params[:controller] != "privacy", params[:action] != "show"
redirect_to "privacy"
end
end

Resources