When I create an article I set a quantity.
Each time an article is sold, its quantity is decreased by a method.
I need to keep my initial stock, the number of sales and the remaining stock...
So my question is:
How can I keep the initial quantity, and the quantity I could add on update?
# article.rb
before_create :quantity_on_create
before_update :quantity_on_update
def quantity_on_create
self.quantity
end
def quantity_on_update
quantity_on_create += self.quantity
end
quantity_on_create remains nil ?
Maybe you could consider an additional attribute for quantity_stock
# article.rb
before_create :quantity_on_create
before_update :quantity_on_update
def quantity_on_create
self.quantity_stock = self.quantity = 10 # initial value
end
def quantity_on_update
if self.quantity > 0
self.quantity -= 1
else
errors.add(:base, 'out of stock')
throw(:abort)
end
end
Related
So I have a User model and a Post model
Post belongs to a User
Post has a field in the database called score
In the Post model I have a method called score which gives the post a score based on the fields (needs to be done this way):
def score
score = 0
if self.title.present?
score += 5
end
if self.author.present?
score += 5
end
if self.body.present?
score += 5
end
score
end
The Question:
There are loads of Users and loads of Posts. So What I'm trying to do is after the score is worked out, I want to save it to the Post score field in the database for each Post. The score should be updated if the user updates the Post.
I have looked at using after_update :score! but don't understand how to apply the logic
It looks a little like you are trying to re-invent the wheel ActiveRecord provides you.
If you have a database field score, then ActiveRecord will automatically provide an attribute_reader and attribute_writer for score and you should not override these unless you have a really really good reason for it, e.g. you need to add some other resources or some serious business logic into it.
There is a way easier way to solve it, by using the before_save hook, which will kick in before any #create or #update:
class Post
attribute_accessible :score # if you have Rails 4.x you can omit this line
before_save :update_score
private
def update_score
new_score = 0
self.score = [:title, :author, :body].each do |field|
new_score += 5 if send(field).present?
end
self.score = new_score
end
This way, ActiveRecord will handle the saving for you and your score will always up to date. Additionally Post#score will always return the real value currently saved in the database
You can do it like this
after_update :score!
def score!
score = 0
if self.title.present?
score += 5
end
if self.author.present?
score += 5
end
if self.body.present?
score += 5
end
self.update_column(:score, score)
end
This is to be done in your Post model.
You can do it using update_column method. Like:
def score
score = 0
if self.title.present?
score += 5
end
if self.author.present?
score += 5
end
if self.body.present?
score += 5
end
self.update_column(:score, score)
end
You need to override the setter method in the Post model
attr_accessible :score
def score=(value)
score = 0
if self.title.present?
score += 5
end
if self.author.present?
score += 5
end
if self.body.present?
score += 5
end
write_attribute(:score, score)
end
I understand what Ruby self means, and I was trying to solve certain challenges on Tealeaf: http://www.gotealeaf.com/books/oo_workbook/read/intermediate_quiz_1
Here is the actual problem:
Snippet 1:
class BankAccount
def initialize(starting_balance)
#balance = starting_balance
end
# balance method can be replaced with attr_reader :balance
def balance
#balance
end
def positive_balance?
balance >= 0 #calls the balance getter instance level method (defined above)
end
end
Now for Snippet 1, running this code:
bank_account = BankAccount.new(3000)
puts bank_account.positive_balance?
prints true on the console, whereas for snippet 2:
Snippet 2:
class InvoiceEntry
attr_reader :product_name
def initialize(product_name, number_purchased)
#quantity = number_purchased
#product_name = product_name
end
# these are attr_accessor :quantity methods
# quantity method can be replaced for attr_reader :quantity
def quantity
#quantity
end
# quantity=(q) method can be replaced for attr_writer :quantity
def quantity=(q)
#quantity = q
end
def update_quantity(updated_count)
# quantity=(q) method doesn't get called
quantity = updated_count if updated_count >= 0
end
end
Now for snippet 2, on running this code:
ie = InvoiceEntry.new('Water Bottle', 2)
ie.update_quantity(20)
puts ie.quantity #> returns 2
Why is this not updating the value?
Why is it working for the first case while not for the second?
You are assigning to quantity the local variable.
If you want to assign to the instance variable (via your def quantity= function) you need to do
self.quantity = updated_count if updated_count >= 0
Essentially, you're making a function call (quantity=) on self.
In snippet 1, balance is a pure function call because there is no assignment going on.
I have a rails model that has a 'percentage' attribute
I would like to make sure the sum of of all 'percentage' is not > 100 when adding new values.
In my model, I have
validate :sum_can_not_exceed_hundred
def sum_can_not_exceed_hundred
if Result.all.sum(:percentage) > 100
errors.add(:base, :sum_can_not_exceed_hundred)
end
end
But this does not work for adding the records that are not on the db and those are are already saved.
Update:
The following seems to work, using a hint from Coderhs
if Result.where.not(id: self.id).sum(:percentage_share) + self.percentage_share > 100
errors.add(:base, :sum_can_not_exceed_hundred)
end
Since your table is new it won't be available on Result.all. Try modifying the code like so
validate :sum_can_not_exceed_hundred
def sum_can_not_exceed_hundred
sum = if self.id
Result.all.sum(:percentage)
else
self.percentage + Result.all.sum(:percentage)
end
errors.add(:base, :sum_can_not_exceed_hundred) if sum > 100
end
end
I'm using optimistic locking on a Rails model. Inside of a loop, I update and save this model (or, rather, many instances of this model).
From inside the loop, I output the "before" and "after" values, and the field appears to be updated correctly. But afterward, when I find the models by ID, I see that the field is not updated. Can anyone spot my error?
class Usage::LeadDistributionWeight < ActiveRecord::Base
attr_accessible :agent_id, :tag_value_id, :weight, :premium_limit, :countdown, :lock_version, :tag_value
def increment_countdown!
self.countdown = self.countdown + self.weight
save
rescue ActiveRecord::StaleObjectError
attempts_to_crement_countdown ||= 0
attempts_to_crement_countdown += 1
self.increment_countdown! unless attempts_to_crement_countdown > 5
false
end
def self.increment_countdowns parent_id, lead_type_id
if lead_type_id.present?
joins(:agent)
.where("#{reflect_on_association(:agent).table_name}.parent_id = ?", parent_id)
.where(tag_value_id:lead_type_id)
.all(readonly:false).each { |weight|
prev = weight.countdown
if weight.increment_countdown!
puts "#{prev} differs from #{weight.countdown}"
else
puts "no difference!"
end
}
end
end
end
I'm currently generating url slugs dynamically for my models (and implementing to_param/self.from_param to interpret them). My slug generation code feels verbose, and could use a refactor.
How would you refactor this so that it is still readable, but less verbose and perhaps more clear?
Relationships
User has_many :lists
List belongs_to :owner
Code
def generate_slug
if self.owner
slug_found = false
count = 0
temp_slug = to_slug
until slug_found
# increment the count
count += 1
# create a potential slug
temp_slug = if count > 1
suffix = "_" + count.to_s
to_slug + suffix
else
to_slug
end
# fetch an existing slug for this list's owner's lists
# (i.e. owner has many lists and list slugs should be unique per owner)
existing = self.owner.lists.from_param(temp_slug)
# if it doesn't exist, or it exists but is the current list, slug found!
if existing.nil? or (existing == self)
slug_found = true
end
end
# set the slug
self.slug = temp_slug
else
Rails.logger.debug "List (id: #{self.id}, slug: #{self.slug}) doesn't have an owner set!"
end
end
You could maybe do this
def generate_slug
return Rails.logger.debug "List (id: #{self.id}, slug: #{self.slug}) doesn't have an owner set!" if !self.owner
count = 1
begin
temp_slug = %Q!#{to_slug}#{"_#{count}" if count > 1}!
existing = self.owner.lists.from_param(temp_slug)
if existing.nil? or (existing == self)
self.slug = temp_slug
end
end while count += 1
end
But there is two things. First you have an infinite loop which is not good. Secondly, instead of looping to check each time if the object exists and that you need to increase your suffix, you better get the last existing list and add just one after that.