How to get object value in ActiveRecord Model? - ruby-on-rails

I want to create custom validation inside The Model.
But nothing in return when i tried to get that value from the variable
This is my model
validate :permanent_event_check
private
def permanent_event_check
param_activity = #activity
# puts "param_activityparam_activityparam_activity"
# puts #activity
# puts param_activity
# puts "param_activityparam_activityparam_activityparam_activity"
if param_activity.permanent == "false"
if param_activity.start_at == "" || param_activity.end_at == ""
#activity.errors[:base] << "You can't leave start and end date blank with Permanent Event"
return false
end
end
end
This is my controller
def create
#activity = admin_current_user.activities.build(activity_param)
if #activity.save
flash[:success] = "Activity Created!"
redirect_to admin_dashboard_url
else
render 'new'
end
end
private
def activity_param
params.require(:activity).permit(:name,:details,:start_at,:end_at,
:activity_image01_url,:activity_image02_url,:activity_image03_url,
:youtube_url,:capacity,:booking_status,:rules,:apply_details,
:payment_price,:payment_need,:avaliable,:rating,:temple_id)
end
But it return nil when i tried to get the value from #activity inside my model.
How can i fix this?
Thanks!

You cannot assign the object like that in the model, instead you van take self.
validates :permanent_event_check
private
def permanent_event_check
if self.permanent == "false"
if self.start_at == "" || self.end_at == ""
self.errors[:base] << "You can't leave start and end date blank with Permanent Event"
return false
end
end
end

I assume that permanent is boolean, start_at and end_at - datetime.
validate :permanent_event_check, unless :permanent
private
def permanent_event_check
# if start_at and end_at are not filled they will be nil which is interpreted as false
unless start_at && end_at
self.errors[:base] << "You can't leave start and end date blank with Permanent Event"
end
end

Related

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.

Update value with first_or_create in rails

I have a table 'Likes' with columns business_id, user_id and liked(0,1) and a function 'change_like_status'.
Now on every function call, If the value is 1 then set it to 0 (or vice versa) and if record doesn't exists then create one with value 1.
The first_or_create method is working just fine but how can i toggle value of column 'liked' while using this method?
Here is my function:
def change_like_status
if current_user.present?
status = Like.where("business_id = ? AND user_id = ?",params['id'],current_user.id).first_or_create(:business_id => params['id'],:user_id => current_user.id,:liked => '1')
abort status.inspect
else
return render :json => {:status => false,:msg=>"You need to sign in before performing this action."}
end
end
In you controller, make the changes
def change_like_status
if current_user
status = Like.create_or_change_status(params[:id], current_user.id)
else
return render json: { status: false, msg: "You need to sign in before performing this action." }
end
end
In your model like.rb file, add a method
def self.create_or_change_status(business_id, user_id)
status = where(business_id: business_id, user_id: user_id).first
if status.nil?
status = create({business_id: business_id, user_id: user_id, liked: 1})
else
status.update_attributes(liked: !status.liked)
end
status
end
def change_like_status
if current_user
current_user.likes.find_by(business_id: params[:id]).switch_status!
else
return render json: { status: false, msg: "You need to sign in before performing this action." }
end
end
class Like
def switch_status!
self.update_column :liked, !liked
end
end
other approach should be something like that
class Like
def switch_status!
self.update_column :liked, !liked
end
end
class User
def likes id
likes_for_business id
end
def likes_for_business(id)
likes.find_by(business_id: id) || likes.create(:business_id: id, liked: true)
end
end
# controller
current_user.likes(params[:id]).switch_status!

How can I redirect after an action is performed in my controller?

I have a form set up to take in date time value. This value will then be sent as a parameter into my controller method "bookingdate" where it will be compared with other dates in the bookings to ensure there is no double booking, using a do loop.
However when I submit the date form, rather than redirect to the next form where a user selects other details it throws me an error or redirects incorrectly.
Here is my controller
def bookingdate
#bookings = Booking.all
#bookings.each do |b|
if b.startdatetime == params[:startdatetime]
#musicians = Musician.where (["id != ?", b.musician_id])
end
end
render :action => 'new'
end
Here is my routes
match '/bookdate', :to => 'bookings#bookingdate'
Add redirect_to helper and pass route to where progress
def bookingdate
#bookings = Booking.all
#bookings.each do |b|
if b.startdatetime == params[:startdatetime]
#musicians = Musician.where (["id != ?", b.musician_id])
end
end
redirect_to path_where_you_want_to_redirect
end
I guess you want to perform some checks, if user has correct filled form
def bookingdate
#bookings = Booking.all
#bookings.each do |b|
if b.startdatetime == params[:startdatetime]
#musicians = Musician.where (["id != ?", b.musician_id])
end
end
if condition_successful
redirect_to path_where_you_want_to_redirect
else
render :bookingdate
end
end

ROR strip issues

i am using strip for check email but it gave me this issues please help me out .
NoMethodError in UsersController#check_email
undefined method `strip' for nil:NilClass
def check_email
if params[:email].blank?
#email = params[:email].strip
user = User.find_by_email(#email)
if user.nil?
text = "false"
else
text = "true"
end
render :text => text
else
render :text =>text
end
end
Based on the feedback by Sergio and Brad:
def check_email
text = params[:email].present? && User.where(email: params[:email].strip).exists?
render :text => text
end
Update it as:--
def check_email
if params[:email].present?
#email = params[:email].strip
user = User.find_by_email(#email)
unless user.present?
text = "false"
else
text = "true"
end
render :text => text
else
render :text =>text
end
end
if params[:email].blank? should be if params[:email].present?.
Problem is when params[:email] is nil you are calling strip method on it i.e. nil.strip
So change it to followin
def check_email
if params[:email].present?
#email = params[:email].strip
user = User.find_by_email(#email)
text = user.nil? ? "false" : "true"
else
text = "please enter email address"
end
render :text =>text
end
You have small error, you need use:
unless params[:email].blank?
Or,
if params[:email].present?
I recommend to read about blank? vs nil? vs empty? vs present?

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)

Resources