Using Rail's Model.update method without providing IDs in the hash - ruby-on-rails

So I understand that you can update some models using Model.update(ids, values) where ids represent the ID and values represent the actual changes. I do also understand that you can pass in a hash, such as {1 => {column_name: "new value"}, 2 => {column_name: "new value"}} which works just fine.
However, what if, instead of IDs, I wanted to use another column such as uuid? Can I use the .update method in such a way to where it would do something like Model.update(uuid: hash.keys, hash.values)?
It doesn't appear that I can do this with this method, but is there another way that I can do this so that I don't have to iterate through every single key and value in my long array (which contains thousands of keys and values)
This is what happens when I try to implement what I would like to do:
[2] pry(#<MyWorker>)> test = {"b7d720f984abeda37836d07b2147560ce06fb4d7e30fe8d59c1fe610cb440bbf" => {:protocol => "udp", :port => 5}}
=> {"b7d720f984abeda37836d07b2147560ce06fb4d7e30fe8d59c1fe610cb440bbf"=>{:protocol=>"udp", :port=>5}}
[3] pry(#<MyWorker>)> Port.update(uuid: test.keys, test.values)
SyntaxError: unexpected ')', expecting =>
...e(uuid: test.keys, test.values)
... ^
[3] pry(#<MyWorker>)>

As Sebastian Palma said, you can do this using a where clause before the update action like so:
Port.where(uuid: test.keys).update(test.values)

Related

Pluck doesn't return a bother records they have the same name

I have a pluck that is turned into a hash and stored in a variable
#keys_values_hash = Hash[CategoryItemValue.where(category_item_id: #category_item.id).pluck(:key, :value)]
If 2 records have the same :key name then only the most recent record is used and they aren't both added to the hash. But if they have the same value and different keys both are added to the hash.
This also occurs if I swap :key and :value around (.pluck(:value, :key)). If they have now the same value it only uses the most recent one and stores that in the hash. But having the same key is now fine.
I'm not sure of this is being caused by pluck or from being sorted in a hash. I'm leaning towards pluck being the culprit.
What is causing this and how can I stop it from happening. I don't want data being skipped if it has the same key name as another record.
I'm not sure why you need convert pluck result into a Hash, because it was an Array original.
Like you have three CategoryItemValue like below:
id, key, value
1, foo, bar1
2, foo, bar2
3, baz, bar3
when you pluck them directly, you will get a array like:
[ ['foo', 'bar1'], ['foo', 'bar2'], ['baz', 'bar3'] ]
but when you convert it into a hash, you will get:
{'foo' => 'bar2', 'baz' => 'bar3' }
because new hash value will override the old one if key ( foo in the example above) exists.
Or you could try Enumerable#group_by method:
CategoryItemValue.where(...).pluck(:key, :value).group_by { |arr| arr[0] }

Rails mongoid query string in array of hashes

I have a Model that is called teams.
When I do Teams.account_ids it returns something like:
[{"_id"=>"145952912234658", "_type"=>"Page"},
{"_id"=>"465641870160985", "_type"=>"Account"}]
Lets say I want to get all Teams that have one specific account id regardless of its _type.
Something like:
Team.where(some_id.in => account_ids.map{|k| k["_id"))
You can use multi-keys to effectively ignore the array when searching and then use the standard "key inside a hash" notation to look at the _ids:
Teams.where('account_ids._id' => { :$in => array_of_ids })

Get value of a key in an array of hashes

I'm fairly new to ruby on rails and I'm having some trouble trying to extract a key value from an array of hashes (#sorted) when using options_from_collection_for_select in my html.haml file.
So far I've tried
options_from_collection_for_select(#sorted, "#{#sorted['id']}",
"#{#sorted['name']}")
options_from_collection_for_select(#sorted, #sorted['id'], #sorted['name'])
But both give me a "Can't convert string to integer" error I've tried calling to_i but the error still persists.
Array of Hashes (#sorted)
#sorted => [{"id"=>"51a7ba4154b3289da800000f", "name"=>"Book1", "count"=>8},
{"id"=>"519d24ed54b328e512000001", "name"=>"Book2", "count"=>5},
{"id"=>"5258917b54b32812cd000003", "name"=>"Book3", "count"=>1}]
With options_for_select:
options_for_select(#sorted.map{|hash| [hash["id"],hash["name"]]})
Remember that when you have an array, it is made up of multiple elements, and the methods you call on it are array methods, not methods for the elements inside.
In this case, each element is a hash, so your array looks like the following:
[ {"id" => 1, "name" => "Name 1"}, {"id" => 2, "name" => "Name 2" ]
The class itself is an array. You can index in to an array like so:
myArray[1]
This method takes an integer and finds the nth element. So doing:
#sorted[1]
Would return this hash element:
{"id" => 2, "name" => "Name 2"}
And now you can call hash methods on it. This is why you were getting that error, because the Array#[] method assumed you were giving it an integer to index in to the array, but you were giving it a string - a string that could not be converted in to an integer.
So in your particular case, you probably mean to do:
#sorted.first["id"], #sorted.first["name"]
(Doing #sorted.first is an alternative to #sorted[0] to get the first element in an array)

=> operator vs = operator

I just started learning ruby on rails, and I'm wondering when I should use "=>" and when I should use "=" for assignment. I am seeing that you use "=>" for hash, for assigning values to symbols in migrations, but i'm not sure where to draw the line.
Thanks!
The => symbol is used solely for hashes. Ruby has a feature in which hashes can be passed as the last argument to a method call without including the surrounding braces. This provides something that resembles keyword arguments (though until Ruby 2.0, Ruby didn't have keyword arguments).
So when you see this:
t.integer :foo, :default => 5
What it really means is this:
t.integer(:foo, { :default => 5 })
The rest is just syntactic sugar designed to make it look nicer.
The = symbol, on the other hand, is the assignment operator you know and love from nearly any programming language.
I struggled with this for a while, but now prefer to use the new style for hashes wherever possible
t.integer :foo, default: 5
t.string :bar, default: 'Dave'
=> is not the same as assignment, but I can see why it is confusing. In a hash you create a key and a value as a pair. The key and value can be anything
{'key1' => 'some value', :symbol_key => 'other value'}
This is different to the assignment, which you can see clearly because if you want the above hash to remain available to your program, you either have to pass it to a method or assign it to a variable
myhash = {'key1' => 'some value', :symbol_key => 'other value'}
And only now can you retrieve stuff from your hash
puts myhash['key1']
So the => operator is actually used to construct hashes (or dictionary objects), assignment allows you to store values in the program.
What is happening quite commonly Rails (and therefore in migrations), is that the hash is being created and passed to the method call without you realising it. But the plumbing is still the same, it's still only a hash that is created.
In Ruby 1.9 you can now define hashes using a javascript-like syntax, so you might start seeing this as well.
myhash = {key1: 'some value', key2: 'other value'}

Using best_in_place gem how do I specify a nil value for a select tag?

The answer on this question has provided me with a nice roadmap for how to generate select tags with data from a collection on an association.
This works nicely and everything is going great.
The issue I have now is, how do I handle an empty collection?
With the regular :type => :input, I can just specify :nil => "Some nil message here".
But that doesn't seem to work for the collection, and to make matters worse, when there is nothing in the collection it seems to be displaying some integers (i.e. 1 and 2). I am assuming those are the IDs from the previously displayed objects in the collection, but for obvious reasons that doesn't make much sense.
Any ideas on how I can handle an empty collection with this gem?
Thanks.
Edit 1:
One alternative is to just put my original best_in_place helper tag inside an if statement for when a collection is not nil. But then how does the user edit it when it is blank? Perhaps there may be no way to handle this, because it would involve creating a new record in the collection.
I use a "workaround" for the empty options in a select tag, it could help you:
:type => :select, :collection => #my_colletion || [[I18n.t('common.none'), -1]]
When #my_colletion is nil, it shows a choice named 'None' with id = -1 (wich is not that bad to handle in the backend).
This part of code assumes the #my_collection is an array of arrays like [ [key, value], [key, value], ... ] OR nil.
Although, if you want your MyModel.all collection to fit the conditions for best_in_place, you can use the following:
#my_collection = MyModel.all.map{ |object| [object.name, object.value] }
# => this returns an array like [ [key, value], [key, value], ... ]
# => returns an empty array (NOT nil) if there is no entry in the DB
About the -1 id:
Using the -1 id as 'none' is easy because you don't need to explicitly handle the value nil (tests, etc). With the -1 id, you can use the following:
MyModel.where(id: params[:id]).first # => Returns the first object that has the params[:id]
# if params[:id] is -1, it will return nil and not raise an error.
I hope it helped :)

Resources