create dynamic default values for column in rails? - ruby-on-rails

I am not sure how to go about this, or if there is a better way to do this, but I have a table called leads(many) and it references agent (one).
I want to make a migration that sets a default value for the agent_id column in the leads table. But i want the default value to loop through all the agent ids. Im not sure how to do this!? Should i use a call back, or should i do in migration file?
Here is the actual question im trying to tackle:
When new leads are created assign it to an agent using a “round robin” That way new leads are distributed evenly across all the agents.
Ive attached a screenshot using SUDO code (i know its not functional as is) as to what I am thinking of doing. Any tips?
(Using ruby on rails w/ postgresql)

I think it makes sense to handle this functionality as part of the main app, and not within migration, as there seem to be a significant chunk of functionality to handle.
Probably best to handle it as part of an after_create callback in the Lead model, and use a class variable to track the next agent to be assigned as follows:
class Lead
# Assign the class variable to the first agent
##next_agent = Agent.first
after_create :set_agent
...
private
# Called by the after_create callback
# Sets the agent_id, and updates the ##next_agent class variable
def set_agent
self.agent_id = ##next_agent.id
##next_agent = find_next_agent
end
## Called from the set_agent method
## Finds the next agent based on the current value of ##next_agent
def find_next_agent
##next_agent = Agent.find(##next_agent.id + 1)
##next_agent = Agent.first unless #next_agent
end
end
The find_next_agent logic above is a simplistic example, assuming that all Agent objects have ids that increment by 1, and there are no gaps (i.e. no deletions in the table).

Related

Dealing with model object updates and synchronizing associated tables

​Hello, I am building a Ruby on Rails cashflow app where the "balance" field in the "accounts" table will be updated based on the "amount" field in the "incomes" table.
How should I deal with Income object updates (when "amount" is changed), so that the "balance" field in "accounts" is updated properly (first decreased by the previous "amount" of the "Income" and then updated with new "amount")?
Is it a good practice to use callbacks in the "Income" model and ActiveModel::Dirty methods such as "income.amount_was" to get the previous value?
I recommend to create a service object, instead of directly updating the Income#amount via income.update in the controller. Updating another model inside some other model's callback IMO increases coupling and debugging complexity as the responsibility bleeds outside its own.
For example, create a IncomeUpdaterService that does this:
class IncomeUpdaterService
def initialize(income)
#income = income
#account = ... # Depends on how they are connected
end
def update(params)
# DB Transaction
# You might want to lock the Account record
...
update_balance(params.amount) if params.amount
...
# DB Transaction Commit
end
private
def update_balance(new_amount)
prev_amount = #income.amount
balance_adjustment = new_amount - prev_amount
#account.balance += balance_adjustment
# I think theres a #account.increment(:balance, balance_adjustment)
# to avoid race condition instead of locking the record, but not sure
# if it's a good practice to use. From what I remember there's some
# kind of warning for using it.
#account.save
end
end
(For the self projecting master developers out there. Just wrote this quickly just to put it out there, don't be too hypercritical on this code as this is just an example and start insulting me, but feel free to suggest some improvements.)

rails variables in production environment

I am developing a rails app. Most of the parts work fine, but I got one weird problem when I tried to calculate the time an user used to edit and submit one form.
I thought it would be good to do it in the following order:
1. in the controller "edit" method, record the time the user start to see the form.
2. in the "update" method, record the submit time, then do the math and get how long the user had spent on the form.
class Test
##start_time = 0
##end_time = 0
def edit
##start_time = Time.now
end
def update
##end_time = Time.now
time_used = ##end_time - ##start_time
puts time_used.to_i
end
end
The code above actually works fine while running on my own developing computer, the output is what I expected. But when I upload the code to the production environment(multicore cpus), sometime the output is right, sometime it is not. I debugged the code and found in some case, the ##start_time is set to 0 when submitting the form. I am confused what was going on, maybe I just misused the ## for the variable. Please help me out, any idea would be appreciated, thanks.
Edit:
The problem is solved by adding a virtual attribute to the model as hinted by Vishal. In addition, I added a hidden field in the submit form, and in the strong parameter part added the corresponding parameter to allow it to be passed from edit to update method.
Your solution will create conflicts when more than two users try to edit simultaneously, So basically what idea I have is:
Add one virtual attribute in your model edit_start_time You don't need attribute for endtime because it can be directly fetched by Time.now at any time.
Set edit_start_time value in edit method like:
#model.edit_start_time = Time.now.utc #you can use any
In update method directly calculate edit time like:
total_update_time = Time.now.utc - #model.edit_start_time.utc
If you are unaware of how to create virtual attributes then there are so many questions on StackOverflow as well as docs. I am not explaining how to do it here because its the different topic.
All the best
You're using class variables that can interfer with each other. Your Test class will only ever have one class variable called ##start_time associated with it.
This means if another user sees the form, they will reset the ##start_time for every user currently on it.
To prevent this, use instance varaibles. When a new user sees the form, they will make a new instance variable that is tied to their instance of the class, rather than the class itself. This will allow users to have different start and end times.0
All you need to do is change every ## to #. So instead of ##start_time', try#start_time` throughout your code, and the same for end_time.

How can I test different app configurations in Rails?

I have an model whose behavior should change slightly based on a configuration file. The configuration file, in theory, will be altered for each installation of the app for my clients. So how can I test for these changes?
For example...
# in app/models/person.rb
before_save automatically_make_person_contributer if Rails.configuration.x.people['are_contributers_by_default']
# in test/models/person_test.rb
test "auto-assigns role if it should" do
# this next line doesn't actually work when the Person#before_save runs...
Rails.configuration.x.people['are_contributers_by_default'] = true
end
test "won't auto assign a role if it shouldn't" do
# this next line doesn't actually work when the Person#before_save runs...
Rails.configuration.x.people['are_contributers_by_default'] = false
end
It doesn't make sense for these to be stored in the database, because they are one time configurations, but I need to make sure my app behaves under all the possible configurations in all environments.
Looks like the way to make this work is to rewrite the Person class so that automatically_make_person_contributer actually performs the evaluation of Rails.configuration.x.people['are_contributers_by_default']. This makes my tests happy and technically doesn't change the way the app works:
# in app/models/person.rb
before_save :automatically_make_person_contributer
private
def automatically_make_person_contributer
if Rails.configuration.x.people['are_contributers_by_default']
# do the actual work here
end
end
However, this means that a value that is going to remain the same for the lifetime of the app's process will be checked every time a Person is created, instead of checked only once at the creation of the Person class.
In my particular case, this tradeoff is fine, but others may want the actual answer to my question.

Rails Limit Model To 1 Record

I am trying to create a section in my app where a user can update certain site wide attributes. An example is a sales tax percent. Even though this amount is relatively constant, it does change every few years.
Currently I have created a Globals model with attributes I want to keep track of. For example, to access these attributes where needed, I could simply do something like the following snippet.
(1+ Globals.first.sales_tax) * #item.total
What is the best way to handle variables that do not change often, and are applied site wide? If I use this method is there a way to limit the model to one record? A final but more sobering question.......Am I even on the right track?
Ok, so I've dealt with this before, as a design pattern, it is not the ideal way to do things IMO, but it can sometimes be the only way, especially if you don't have direct disk write access, as you would if deployed on Heroku. Here is the solution.
class Global < ActiveRecord::Base
validate :only_one
private
def only_one
if Global.count >= 1
errors.add :base, 'There can only be one global setting/your message here'
end
end
end
If you DO have direct disk access, you can create a YAML config file that you can read/write/dump to when a user edits a config variable.
For example, you could have a yaml file in config/locales/globals.yml
When you wanted to edit it, you could write
filepath = "#{Rails.root}/config/locales/globals.yml"
globals = YAML.load(File.read("#{Rails.root}/config/locales/globals.yml"))
globals.merge!({ sales_tax: 0.07 })
File.write(filepath) do |f|
f.write YAML.dump(globals)
end
More on the ruby yaml documentation
You could also use JSON, XML, or whatever markup language you want
It seems to me like you are pretty close, but depending on the data structure you end up with, I would change it to
(1+ Globals.last.sales_tax) * #item.total
and then build some type of interface that either:
Allows a user to create a new Globals object (perhaps duplicating the existing one) - the use case here being that there is some archive of when these things changed, although you could argue that this should really be a warehousing function (I'm not sure of the scope of your project).
Allows a user to update the existing Globals object using something like paper_trail to track the changes (in which case you might want validations like those presented by #Brian Wheeler).
Alternatively, you could pivot the Global object and instead use something like a kind or type column to delineate different values so that you would have:
(1+ Globals.where(kind: 'Colorado Sales Tax').last) * #item.total
and still build interfaces similar to the ones described above.
You can create a create a class and dump all your constants in it.
For instance:
class Global
#sales_tax = 0.9
def sales_tax
#sales_tax
end
end
and access it like:
Global.sales_tax
Or, you can define global variables something on the lines of this post

Class variables VS constants in Rails models

In my application there can be only one current Event which defaults to the nearest date event. I need to retrieve this event in various places and since it doesn't change it makes sense to cache it. There are two ways of doing it known to me:
class Event < ActiveRecord::Base
CURRENT_EVENT = Event.where('starts_on >= ?', Time.now).
order('starts_on ASC').limit(1).first
# OR
def self.current_event
##current_event ||= Event.where('starts_on >= ?', Time.now).
order('starts_on ASC').limit(1).first
end
end
Which one would be the best? Or any other alternatives? I know that using ## class variables is not recommended since they are not thread safe.
I guess you aren't right about your approach: this way your app will keep your cached value forever. New events won't affect it which is completely wrong. It may be the situation when some event already passed but it is still cached as "current".
By the way: limit(1).first does the same as the only first.
Neither first nor second approach are correct. If you define constant - it will find Event, actual on Rails initialization process time. Second approach will not cache your record.
As for me, this is not so fat data to cache.

Resources