Rails Check If Record Is First - ruby-on-rails

I am iterating through a list of records. I need to check that if a record is first do XYZ and if not do ABC. Unfortunately I cant do this:
user = User.first
or
user = User.find(:id)
user.first?
Solution posted below

1. Make method to grab next and previous records
def next
[Model].where("id > ?", id).first
end
def prev
[Model].where("id < ?", id).last
end
2. Make method to check if record is first
def first?(record)
[Model].first == record
end
3. check if record is first
records.each do |record|
if record.first?(record)
record.update_attributes(attr: record.attr + record.attr)
else
prev_rec = [Model].find(record.id).prev
record.update_attributes(attr: prev_rec.attr + record.attr )
end
end
returns true or false

One improvement i would make sure that [Model].first is persistent so that it doesn't make a call to the database each time the loop is run.

Related

Ignore parameters that are null in active record Rails 4

I created a simple web form where users can enter some search criteria to look for venues e.g. a price range. When a user clicks "find" I use active record to query the database. This all works very well if all fields are filled in. Problems occur when one or more fields are left open and therefore have a value of null.
How can I work around this in my controller? Should I first check whether a value is null and create a query based on that? I can imagine I end up with many different queries and a lot of code. There must be a quicker way to achieve this?
Controller:
def search
#venues = Venue.where("price >= ? AND price <= ? AND romance = ? AND firstdate = ?", params[:minPrice], params[:maxPrice], params[:romance], params[:firstdate])
end
You may want to filter out all of the blank parameters that were sent with the request.
Here is a quick and DRY solution for filtering out blank values, triggers only one query of the database, and builds the where clause with Rails' ActiveRecord ORM.
This approach safeguards against SQL-injection, as pointed out by #DanBrooking. Rails 4.0+ provides "strong parameters." You should use the feature.
class VenuesController < ActiveRecord::Base
def search
# Pass a hash to your query
#venues = Venue.where(search_params)
end
private
def search_params
params.
# Optionally, whitelist your search parameters with permit
permit(:min_price, :max_price, :romance, :first_date).
# Delete any passed params that are nil or empty string
delete_if {|key, value| value.blank? }
end
end
I would recommend to make method in Venue
def self.find_by_price(min_price, max_price)
if min_price && max_price
where("price between ? and ?", min_price, max_price)
else
all
end
end
def self.find_by_romance(romance)
if romance
where("romance = ?", romance)
else
all
end
end
def self.find_by_firstdate(firstdate)
if firstdate
where("firstdate = ?", firstdate)
else
all
end
end
And use it in your controller
Venue
.find_by_price(params[:minPrice], params[:maxPrice])
.find_by_romance(params[:romance])
.find_by_firstdate(params[:firstdate])
Another solution to this problem, and I think a more elegant one, is using scopes with conditions.
You could do something like
class Venue < ActiveRecord::Base
scope :romance, ->(genre) { where("romance = ?", genre) if genre.present? }
end
You can then chain those, which would work as an AND if there is no argument present, then it is not part of the chain.
http://guides.rubyonrails.org/active_record_querying.html#scopes
Try below code, it will ignore parameters those are not present
conditions = []
conditions << "price >= '#{params[:minPrice]}'" if params[:minPrice].present?
conditions << "price <= '#{params[:maxPrice]}'" if params[:maxPrice].present?
conditions << "romance = '#{params[:romance]}'" if params[:romance].present?
conditions << "firstdate = '#{params[:firstdate]}'" if params[:firstdate].present?
#venues = Venue.where(conditions.join(" AND "))

Rails Remove Model from ActiveRecord::Relation Query

What's the best way to dynamically remove a model from a query? Basically I want to find all campaigns where a user hasn't already provided a response.
The below method delete_at actually deletes the model which isn't what I want. I only want it remove from the local 'campaigns' ActiveRecord::Relation query set that I got.
def self.appuser_campaigns appuser_id, language
appuser = Appuser.find(appuser_id)
campaigns = Campaign.check_language language
i = -1
campaigns.each do |campaign|
i = i + 1
responses = Response.where(appuser_id: appuser_id, campaign_id: campaign.id)
if responses.length > 0
campaigns.delete_at(i)
end
end
puts campaigns.class.name #"ActiveRecord::Relation"
campaigns
end
def self.check_language language
campaigns = Campaign.where(language: language, status: "In Progress")
end
You can do the following:
already_answered_campaign_ids = Appuser.find(appuser_id).responses.pluck(:campaign_id)
Campaign.where('id NOT IN (?)', already_answered_campaign_ids.presence || -1)

Get row of inserted id in rails transaction with postgres

I have the following code:
#which receives Group objects and saves them in a transaction
def saveGroup(group_buffer)
self.transaction do
output = group_buffer.each(&:save)
if(!output)
return false
break
elsif( #I want the inserted row id as they are inserted)
#put inserted row id in array
end
end
return #the_array
end
Is this possible? Basically, what I want is to obtain the inserted row id in a transaction as the objects are saved and push it into an array. What is the best way to do this? Thank you very much...
Is this what you are looking for? if any instance will not save, the transaction will roll back, so you don't have to brake it manually.
def saveGroup(group_buffer)
ids = []
self.transaction do
group_buffer.each do |i|
i.save
ids << i.id
end
end
if ids.empty?
false
else
ids
end
end

How to query many fields, allowing for NULL

I have a Rails site that logs simple actions such as when people upvote and downvote information. For every new action, an EventLog is created.
What if the user changes his or her mind? I have an after_create callback that looks for complementary actions and deletes both if it finds a recent pair. For clarity, I mean that if a person upvotes something and soon cancels, both event_logs are deleted. What follows is my callback.
# Find duplicate events by searching nearly all the fields in the EventLog table
#duplicates = EventLog.where("user_id = ? AND event = ? AND project_id = ? AND ..., ).order("created_at DESC")
if #duplicates.size > 1
#duplicates.limit(2).destroy_all
end
The above code doesn't quite work because if any of the fields happen to be nil, the query returns [].
How can I write this code so it can handle null values, and/or is there a better way of doing this altogether?
If I understood this correctly,
some of the fields can be nil, and you want to find activity logs that have same user_id, same project_id or project id can be nil.
So I guess this query should work for you.
ActivityLog.where(user_id: <some_id> AND activity: <complementary_id> AND :project_id.in => [<some_project_id>, nil] ....)
This way you would get the complementary event logs where user_id is same and project id may or may not be present
class ActivityLog
QUERY_HASH = Proc.new{ {user_id: self.user_id,
activity: complementary_id(self.id),
and so on....
} }
How about:
# event_log.rb
def duplicate_attr_map
{
:user_id,
:project_id
}
end
def duplicates
attribs = duplicate_attr_map.reject_if(&:blank?)
query = attribs.map { |attr| "#{attr} = ?" }.join(' AND ')
values = attribs.map { |attr| self.send(attr) }
EventLog.where(query, *values).order("created_at DESC")
end
def delete_duplicates(n)
duplicates.limit(n).delete_all if duplicates.size > 1
end
# usage:
# EventLog.find(1).delete_duplicates(2)
not tested, could be improved

Rails validate association only when loaded

I have an activity model which has_many participants and I'd like to ensure that a participant always exists when updating an activity and its participants. I have the following method in my activity model which does the trick:
def must_have_participant
if self.participants.size == 0 || self.participants.size == self.participants.to_ary.find_all{ |p| p.marked_for_destruction? }.count
self.errors[:base] << I18n.t(:msg_activity_must_have_participant)
end
end
The problem is that the participants are lazy loaded if I'm simply updating the activity on its own which I'd like to avoid. I've tried the following alternative, however, loaded? returns false when removing all participants using the :_destroy flag.
def must_have_participant
if self.new_record? || self.participants.loaded?
if self.participants.size == 0 || self.participants.size == self.participants.to_ary.find_all{ |p| p.marked_for_destruction? }.count
self.errors[:base] << I18n.t(:msg_activity_must_have_participant)
end
end
end
Is there an alternative to loaded? that I can use to know whether the participants are going to be updated?
I did something like this in a recent validation that I created. I searched for the original record and checked the original value against the new value. No guarantees my code will work for you but here is my code for your application:
orig_rec = self.find(id)
if participant_ids.size != orig_rec.participant_ids.size
Note that I checked the size of participant_ids instead of fetching all the participant records and checking the size of them. That should be more efficient.
I don't know if there is some kind of built in function to do this or not in ruby, I'll be curious to see what someone who is more rails specific may suggest.
For reference I've amended the method like so:
def must_have_participant
if self.new_record? || self.association(:participants).loaded?
if self.participants.size == 0 || self.participants.size == self.participants.select{ |p| p.marked_for_destruction? }.size
self.errors[:base] << I18n.t(:msg_must_have_participant)
end
end
end

Resources