My method loads a list of countries(code, name) into the database, but before that it has to check, if the country data does not already exist. This works fine:
def self.load_countries
get_countries.each do |country|
code, name = country
if find_by_code(code).nil?
create({ 'name' => name, 'code' => code })
end
end
end
However, as I am new to Ruby, I want to learn the best practises. So, in this code I am not sure about two things which might be (or might not be) optimised:
This question might be silly, but I want to be sure: when I start the loop with the get_countries.each, is it ok to use a method instead of a variable? Isn't the same method called each cycle (N times)? In other words, would this be anymore efficient:
countries = get_countries
countries.each do |country|
Any comments on those few lines of code are welcome, since the fact it works doesn't necessarily mean that I am doing it the right way.
Thank you.
You can use the exists? function in ActiveRecord.
def self.load_countries
get_countries.each do |country|
code, name = country
unless exists?(:code => code)
create({ :name => name, :code => code })
end
end
end
The get_countries function gets called only once. It returns an enumerable data type and then the each walks through each of them.
Use find_or_create_by
get_countries.each do |country|
code, name = country
find_or_create_by_code_and_name(code, name)
end
1) Add uniqueness validation to your model (assuming Rails 3)
validates :code, :uniqueness => true
Use db/seeds.rb for loading the seed data to database. IMHO 'load_countries' method doesn't belong to model (especially if it is a one time operation).
In Ruby on rails, we have four methods to check if a record exists in the database or not?
.present?
It is the most time-consuming method since it returns all records from the database.
.any? / .empty?
The above both have same performance efficiency since both of them finally fire a 'COUNT' query on the database. Hence this both are efficient when compared with .present?
.exist?
The last one is even more optimized, and it should be your first choice when checking the existence of a record. It uses the 'SELECT 1 ... LIMIT 1' approach.
Remember one thing when your ActiveRecord objects are already in memory (If you preloaded them) then do not use 'exist?' rather use 'any?', since 'exist?' always hit database in respect of whether the object is in-memory or not, whereas 'any? / empty?' fill not hit the database again if the records are already loaded into memory.
You can also refer to this article : Check if record is exist in ROR
Maybe you have to use UNIQUE for codes in your database model?
I mean this http://ar.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M000086
You can use unique validation in your ActiveRecord model eg:
class Country < ActiveRecord::Base
validates_uniqueness_of :code
validates_uniqueness_of :name
end
1.) You don't necessarily need to select the whole row, and you could use the :select=> option to limit the columns you fetch, but this seems like a micro-optimization to me. I wouldn't worry about it. If the efficiency of this method bothers you that much, you'd be better off figuring out a way to avoid making an SQL query within a loop; for example, consider selecting all the existing countries before the loop, storing them in an array or hash, and using that to see if the country already exists. Then instead of dozens of trips to the database, you'd only make one (not counting those where you add new records). On the other hand, this doesn't sound like the kind of code you're going to be running lots of times (it sounds like a case of seeding a database table), so it might not matter much.
2.) No, the get_countries method won't get called each iteration of the loop, only once before the #each starts; Assuming get_countries returns an Array, #each is a method on the array.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.