I have the following:
after_action :prevent_order_create, :only => [:update, :create]
...
private
def prevent_order_create
#order = Order.find(params[:id]) #line 270
#listing = Order.find_by(params[:listing_id])
#order_seller = #order.where(order_status: [1]).where(:seller)
if #order_seller.count >= 0
#listing.update_column(:listing_status, 3)
end
end
The goal is to limit the amount of orders that can be open for any 1 seller.
When I use this code, I get the following error:
ActiveRecord::RecordNotFound (Couldn't find Order without an ID):
app/controllers/orders_controller.rb:270:in `prevent_order_create'
The order gets created but for whatever reason this issue is happening. Shouldn't the method be getting checked AFTER the order is made without issues?
btw using 0 for testing purposes.
EDIT:
I did:
def prevent_order_create
#order_seller = Order.where(order_status: [1]).where(listing_id: Listing.ids)
if #order_seller.count >= 10
#listing.update_column(:listing_status, 3)
end
end
Seems to work for now. Will update.
Everything you describe is as expected.
An after action is run after the action is already executed, so the record has already been persisted to your DB by the time the exception happens.
The exception is caused because params[:id] is nil, which makes sense for a create action.
Update after clarification in comment
OP said:
I want after create and update... to find any orders with matching :listing_id's and with order_status's of [1] to then count those records. If the count is >= 1000, to then change the listing_status of the Listing
I think the way I would do this is in an after_save in the Order model. Based on your latest edit something like:
scope :status_one, -> { where order_status: 1 } # I'm guessing this should be 1 not [1] as you keep putting
after_save :update_listing
def update_listing
if listing.orders.status_one.count >= 10
listing.update! listing_status: 3
end
end
So that's basically saying that if the associated listing has 10 or more associated orders with order_status=1 then set its status to 3.
Related
I have been facing one serious issue in Ruby on Rails code for quite a long time. The active record records the transactions redundantly sometimes upto 3 times but this doesn't happen to all users. Let's say out of 100 upto 3 are affected. Surprisingly since i am using timestamp to save transaction ids all redundant rows often are recorded with the same id not even a difference of single second between all of them.
skip_before_action :verify_authenticity_token
before_action :restrict_access, except: [:authenticate, :go_success, :go_failed]
def creategoal
goal = #user_info.goal.create(days: 365)
if goal.save
goal.goals_mandates.create(reference: "000-000-GG-#{goal.id}",
price_dd: params[:amount],
mandate_attempt_date: Date.today,
mandate_active: false)
self.initTrans(goal)
end
end
def initTrans(goal)
userType = UserType.where(name: "partner").first
if userType.user_info_types.where(user_info_id: #user.id).first.nil?
#user.user_info_types.create(user_type_id: userType.id)
end
transaction_id = "GG-"+ Time.now.to_i.to_s
transaction = goal.goal_transaction.create(batch_id: transaction_id,
response_batch: transaction_id,
amount_deducted: session[:amount_to_save],
transaction_type: "direct_debit",
merchant: #accessor.name)
if transaction.save
redirect_to "/success?trans_id="+transaction_id
else
redirect_to "/failed"
end
end
I will be grateful if anyone genius out there can help me out.
I'm having trouble trying to isolate the next/previous record in a collection. I'm self-taught and relatively new to Rails/coding.
I have a Goal class which has many GoalTasks.
GoalTask has taskduedate. I want to be able to cycle next/previous on the goal_tasks, based on their taskduedate.
The issue is that a task due date is just when the task is due to be completed, but it can be set at any time and may not be in sequential order so that I don't know what else to order it by to correctly cycle through it.
I have created an array of goal_tasks to identify which one is currently being viewed (e.g. Task: 3/20), so I could use that to go to the next one, I think there might be a solution here, but it feels wrong to handle it in the view/controller?
I've tried the below solution from stackoverflow, but it doesn't handle the fact that I have multiple goal_tasks due on the same day, if I click next it just goes to the next day that goal_tasks are due. e.g. if I have three tasks due today and I'm on the first one and click next, it will just skip over the other two for today.
I then tried to add the >= (displayed below) to try and pull the next task (including those on the same day), and I've tried to ignore the current task by doing where created_at is not the same as the current goal_task and where.not, but I haven't managed to successfully get it to cycle the way I want it to, and I imagine there's a better solution.
GoalTasksController:
def show
#all_tasks_ordered_due_date_desc = #goal.goal_tasks.order('taskduedate ASC', 'id ASC')
end
show.html.erb:
Task: <%= #all_tasks_ordered_due_date_desc.find_index(#goal_task) +1 %> /
<%= #goal.goal_tasks.count%>
GoalTask.rb
scope :next_task, lambda {|taskduedate| where('taskduedate >= ?', taskduedate).order('id ASC') }
scope :last_task, lambda {|taskduedate| where('taskduedate <= ?', taskduedate).order('id DESC') }
def next_goal_task
goal.goal_tasks.next_task(self.taskduedate).first
end
Thanks
I used the method found here: Rails 5: ActiveRecord collection index_by
Which meant adding a default scope and changing GoalTask.rb to:
default_scope { order('taskduedate ASC') }
def next_goal_task
index = goal.goal_tasks.index self
goal.goal_tasks[index + 1]
end
def last_goal_task
index = goal.goal_tasks.index self
goal.goal_tasks[index -1]
end
I have a resource :posts, which I show one at a time in show.html.erb
Suppose I have ten posts, each with an :id going from 1-10. If I delete post #2, then my posts will be 1,3,4,5,6,7,8,9,10. If I create ten posts and delete them all, then the next post :id would be [1,3..10,21] but I would only have 11 posts.
I want to show the post number that's in the application and put it in the view against a total number of posts. So if you were looking at post #3, it might have an :id of 3, but it is post #2 in the database.
Here's what I tried so far:
posts_controller.rb
def show
...
#post = Post.friendly.find(params[:id])
#total_posts = Post.all.count.to_i
#posts_array = Post.pluck(:id).to_a
...
end
views/posts/show.html.erb
<%= #post.id %> of <%= #total_posts %> /
models/post.rb
def next
Post.where("id > ?", id).order(id: :asc).limit(1).first
end
def prev
Post.where("id < ?", id).order(id: :desc).limit(1).first
end
However, showing the :id of a resource is a security issue so I don't know how to do it better.
How can I make it so the show.html.erb view only shows the current index order of the total amount of resources as compared to the post_id?
An efficient way to do this could be
# app/controllers/posts_controller.rb
def show
#post = Post.friendly.find(params[:id])
#total_posts = Post.count
#post_index = Post.where("id <= ?", #post.id).count
end
# app/views/posts/show.html.erb
. . .
<%= #post_index %> of <%= #total_posts %>
. . .
You should avoid loading all posts (or even their id) if you can. This will become more and more expensive as the number of posts grows and will eventually become a bad bottleneck for performance.
If you're trying to find the 'array index' of a record (so to speak) you can do this:
Agency.order(id: :asc).offset(params[:index]).limit(1)
You don't really want to do any other way because then it will load EVERY record into rails which will be very slow. It's better to ask the database for only a single record (which is what 'offset' does). Just replace params[:index] with whatever the name of the params is, whether its params[:id], etc.
I did just want to address one thing you said:
However, showing the :id of a resource is a security issue so I don't know how to do it better
That's not a security issue. The app should be designed in a way where the ID of a resource is not special or "secret." If you have an ID of a record, your controller should work such that it "authorizes" certain actions and won't let you do something you're not supposed to (like a user deleting a post).
If you REALLY need to do this, then just hide the ID and use a slug instead, like example.com/this-is-a-post-slug. This can be done quite easily
Edit To answer your specific question...
ids = Agency.order(id: :asc).pluck(:id)
#post_index = ids.find_index(#post.id)
#next_post = ids[#post_index + 1]
#prev_post = ids[#post_index - 1]
You can now use #post_index in your view.
Note: #prev_post and #next_post will be nil when the page doesn't exist (i.e. the "next post" when you're on the last page), so you will need to check that.
Just try it:
def show
...
#post = Post.friendly.find(params[:id])
#total_posts = Post.count # this will return integer type data
#posts_array = Post.pluck(:id) # you don't need to_a as .pluck returns array
...
For the next part you could write:
def next
self.class.where("id > ?", id).limit(1).first # this use of id is secured.
end
def prev
self.class.where("id < ?", id).order(id: :desc).limit(1).first
end
i am trying to work out how to write a rake tasks that will run daily and find where the days remaining is 0 to update the column amount to zero.
I have the following methods defined in my model, though they don't exactly appear to be working as I am getting the following error in the view
undefined method `-#' for Mon, 27 Jun 2016:Date
def remaining_days
expired? ? 0 : (self.expire_at - Date.today).to_i
end
def expired?
(self.expire_at - Date.today).to_i <= 0
end
def expire_credits
if expired?
self.update(:expire_at => Date.today + 6.months, :amount => 0)
end
end
with the rake tasks i have never written of these and i thought i would be able to call a method of StoreCredit that would expire the points if certain conditions are met but i am not sure how this all works
task :expire_credits => :environment do
puts 'Expiring unused credits...'
StoreCredit.expire_credits
puts "done."
end
# model/store_credit.rb
# get all store_credits that are expired on given date, default to today
scope :expire_on, -> (date = Date.current) { where("expire_at <= ?", date.beginning_of_day) }
class << self
def expire_credits!(date = Date.current)
# find all the expired credits on particular date, and update all together
self.expire_on(date).update_all(amount: 0)
end
end
Since it's a rake task, I think it's more efficient to update all expired ones together
#rake file
result = StoreCredit.expire_credits!
puts "#{result} records updated"
Retrieve Record Count Update
class << self
def expire_credits!(date = Date.current)
# find all the expired credits on particular date, and update all together
records = self.expire_on(date)
records.update_all(amount: 0)
records.length
end
end
You call class method but define instance method. You will need to define class method:
def self.expire_credits
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.