I think the best way for me to explain this question is with example. Here is a simple method with a hash:
def getValues(table)
allColumns = {
'User' => ['first_name', 'last_name'],
'Vehicle' => ['make', 'model', 'id'],
}
end
I am trying to pass in the table and based on that table return a range of values. I would like to know what would be (performance-wise) the best way to accomplish this. Is it using a switch statement, if/else, some sort of loop? If you come up with an answer, please be as kind to include an example so that I may understand better.
I suggest you to rename the parameter first, maybe to table_name or something more descriptive.
Second it is kind of a convention in ruby to use method names separated by _, and avoid using camelCase as another languages.
Third, i would put the list on a constant variable or something, and just reference it inside the method.
Then you can look up the values of some hash based on a key just by using hash[key].
LIST = {
'User' => ['first_name', 'last_name'],
'Vehicle' => ['make', 'model', 'id'],
}
def get_values(table_name)
LIST[table_name]
end
Hash lookup by key is probably one of the most performant operations you could do with a collection, so there is no need to worry about it.
Related
Does anyone know if/how you can reference an attribute of an ActiveRecord model when updating a model using a symbol? I have the code below, which updates my SocialMediaPost model where call[:model_attribute] is a symbol equal to :facebook_like_count.
I want to do something like this:
SocialMediaPost.find(id).update_columns(call[:model_attribute]: resp.size)
That is functionally equivalent to this:
SocialMediaPost.find(id).update_columns(facebook_like_count: resp.size)
Note: I need to use update_columns for this particular task to bypass all my before_save, after_save, and after_commit callbacks.
Note that
{ name: value }
is a special shortcut notation you can use for maps where the keys are symbols. It is equivalent to the following notation
{ :name => value }
If you use anything else than a symbol you must use the "hash rocket" notation, e.g.
"name" => value
So in your case you can use
SocialMediaPost.find(id).update_columns(call[:model_attribute] => resp.size)
Please try this:
SocialMediaPost.find(id).update_columns(call[:model_attribute].to_sym => resp.size)
You can use #to_sym:
call[:model_attribute].to_sym
I have some code that is chugging through a set of Rails Active Record models, and setting an attribute based on a related value from a 2D Array.
I am essentially setting a US State abbreviation code in a table of US States which was previously only storing the full names. A library of state names is being used to derive the abbreviations, and it contains a 2D Array with each sub-array having a full name, and an abbreviation (i.e., [['New York', 'NY']['Pennsylvania', 'PA'][etc]]). I compare the state name from each record in the database to each full text name in this Array, then grab the corresponding sibling Array cell when there is a match.
This code works fine, and produces the correct results, but its frumpy looking and not easily understood without reading many lines:
# For the following code, StatesWithNames is an Active Record model, which is
# having a new column :code added to its table.
# Sates::USA represents a 2D Array as: [['StateName', 'NY']], and is used to
# populate the codes for StatesWithNames.
# A comparison is made between StatesWithNames.name and the text name found in
# States::USA, and if there is a match, the abbreviation from States::USA is
# used
if StatesWithNames.any?
StatesWithNames.all.each do |named_state|
if named_state.code.blank?
States::USA.each do |s|
if s[0] == named_state.name
named_state.update_column(:code, s[1])
break
end
end
end
end
end
What is the most Ruby style way of expressing assignments with logic like this? I experimented with a few different procs / blocks, but arrived at even cludgier expressions, or incorrect results. Is there a more simple way to express this in fewer lines and/or if-end conditionals?
Yea, there is a few ifs and checks, that are not needed.
Since it is Rails even though it does not state so in question's tags, you might want to use find_each, which is one of the most efficient way to iterate over a AR collection:
StatesWithNames.find_each do |named_state|
next unless named_state.code.blank?
States::USA.each do |s|
named_state.update_column(:code, s[1]) if s[0] == named_state.name
end
end
Also be aware, that update_column bypasses any validations, and if you wish to keep your objects valid, stick to update!.
And last thing - wrap it all in transaction, so if anything goes wrong all the way - it would rollback any changes.
StatesWithNames.transaction do
StatesWithNames.find_each do |named_state|
next unless named_state.code.blank?
States::USA.each do |s|
named_state.update!(:code, s[1]) if s[0] == named_state.name
end
end
end
You might use a different data structure for this.
With your existing 2D array, you can call to_h on it to get a Hash where
a = [['California', 'CA'], ['Oregon', 'OR']].to_h
=> { 'California' => 'CA', 'Oregon' => 'OR' }
Then in your code you can do
state_hash = States::USA.to_h
if StatesWithNames.any?
StatesWithNames.all.each do |named_state|
if named_state.code.blank?
abbreviation = state_hash[named_state.name]
if !abbreviation.nil?
named_state.update_column(:code, abbreviation)
end
end
end
end
the first thing you want to do is convert the lookup from an array of arrays to a hash.
state_hash = States::USA.to_h
if StatesWithNames.any?
StatesWithNames.all.select{|state| state.code.blank?}.each do |named_state|
named_state.update_column(:code, state_hash[named_state.name]) if state_hash[named_state.name]
end
end
I want to implement a method that checks if a model's instance has only nil or empty attributes, except from its id or timestamps.
I've made use of an auxiliary method that removes a key from Hash and return the remaining hash ( question 6227600)
class ActiveRecord::Base
def blank?
self.attributes.remove("id","created_at","updated_at").reject{|attr| self[attr].blank?}.empty?
end
end
I guess that there may be much simpler, efficient or safer way to do this. Any suggestion?
def blank?
self.attributes.all?{|k,v| v.blank? || %w(id created_at updated_at).include?(k)}
end
My response is almost the same that tadman gave, but expressed in a more concise way.
Be careful with two situations:
- **blank?** is not a good choice as name, since if you call **object_a.object_b.blank?** trying to know if there is or not a object_b inside object_a, you'll get true event if the object exists. **empty?** seems a better name
- If databases sets defaults values, it can be tricky.
EDIT: Since build an array every iteration is slow (thanks tadman), a beter solution is:
def empty?
ignored_attrs = {'id' => 1, 'created_at' => 1, 'updated_at' => 1}
self.attributes.all?{|k,v| v.blank? || ignored_attrs[k]}
end
You could just check that all the properties in the attributes hash are not present, or the converse:
class ActiveRecord::Base
def blank?
!self.attributes.find do |key, value|
case (key)
when 'id', 'created_at', 'updated_at'
false
else
value.present?
end
end
end
end
Unfortunately this will not account for things that are set with a default in your database, if any relationship keys are assigned, among other things. You will have to add those as exceptions, or compare the values to a known default state of some sort.
This sort of thing is probably best implemented on a case by case basis.
I am trying to accomplish the following in Ruby:
person_struct = StructWithType.new "Person",
:name => String,
:age => Fixnum,
:money_into_bank_account => Float
And I would like it to accept both:
person_struct.new "Some Name",10,100000.0
and
person_struct.new "Some Name","10","100000.0"
That is, I'd like it to do data conversion stuff automatically.
I know Ruby is dinamically and I should not care about data types but this kind of conversion would be handy.
What I am asking is something similar to ActiveRecord already does: convert String to thedatatype defined in the table column.
After searching into ActiveModel I could not figure out how to to some TableLess that do this conversion.
After all I think my problem may require much less that would be offered by ActiveModel modules.
Of course I could implement a class by myself that presents this conversion feature, but I would rather know this has not yet been done in order to not reinvent the wheel.
Tks in advance.
I think that the implementation inside a class is so easy, and there is no overhead at all, so I don't see the reason to use StructWithType at all. Ruby is not only dynamic, but very efficient in storing its instances. As long as you don't use an attribute, there is none.
The implementation in a class should be:
def initialize(name, age, money_into_bank_account)
self.name = name
self.age = age.to_i
self.money_into_bank_account = money_into_bank_account.to_f
end
The implementation in StructWithType would then be one layer higher:
Implement for each type a converter.
Bind an instance of that converter in the class.
Use in the new implementation of StructWithType instances (not class) the converters of the class to do the conversion.
A very first sketch of it could go like that:
class StructWithType
def create(args*)
<Some code to create new_inst>
args.each_with_index do |arg,index|
new_value = self.converter[index].convert(arg)
new_inst[argname[index]]= new_value
end
end
end
The ideas here are:
You have an instance method named create that creates from the factory a new struct instance.
The factory iterates through all args (with the index) and searches for each arg the converter to use.
It converts the arg with the converter.
It stores in the new instance at the argname (method argname[] has to be written) the new value.
So you have to implement the creation of the struct, the lookup for converter, the lookup for the argument name and the setter for the attributes of the new instance. Sorry, no more time today ...
I have used create because new has a different meaning in Ruby, I did not want to mess this up.
I have found a project in github that fulfill some of my requirements: ActiveHash.
Even though I still have to create a class for each type but the type conversion is free.
I am giving it a try.
Usage example:
class Country < ActiveHash::Base
self.data = [
{:id => 1, :name => "US"},
{:id => 2, :name => "Canada"}
]
end
country = Country.new(:name => "Mexico")
country.name # => "Mexico"
country.name? # => true
In my Article model (rails 3), I want to create methods that takes a parameter.
So I can do things like:
Article.get_by_id(:cache => true, :cache_expire => some_date)
I want to re-use this parameter, how can I do this?
The parameter is a hash of options, and I want to use it in many of my controllers methods.
you can add following method to Article model
self.get_by_id(options = {})
cache = options['cache']
end
Now the parameter sent to the above method can be accessed using option hash. i.e, options['cache'] and options['cache_expired'].
Do you mean like this?
custom_options = { :cache => true, :cache_expire => some_date }
Article.get_by_id(custom_options)
I'm sorry if I am not getting you but your question was very brief.