My database will not update my active_quests. All I am trying to do is replace one array of hashes with another, updated array of hashes. I assumed would be the simplest way of handling this. Here's the code:
# construct the query
query = Player.where( :_id => player_id).fields( :xp, :lvl_prgrssns, :active_quests, :completed_quests )
# get the player
player = query.first
if !player.nil?
return_val = player.set( :active_quests => [{"quest_id" => "123"}, {"quest_id" => "456"}])
logger.debug "return_val = "+return_val.to_s # comes out as 180
end
My understanding is that, if the return from a set is positive, that means that the set was successful. It returns as 180 in this simplified case but the active_quests never get updated on the player. I can go into the mongo console and execute this:
db.players.update({_id:ObjectId("50756b1896f4f5121a00000a")}, {$set:{active_quests:[{"quest_id":"1"}, {"quest_id":"2"}] }});
and active_quests will update as expected but no matter what I try in rails the update appears to go through but nothing updates.
Here are some of the many alternatives I have tried (all have been tried with and without .to_mongo and with and without player.save after them):
Player.where( :_id => params[:player_id] ).update(:active_quests => active_quests_list.to_mongo)
player.update_attributes(:active_quests => active_quests_list.to_mongo)
player_update = player.as_json
player_update["active_quests"] = active_quests_list
player.update_attributes(player_update)
return_val = query.update( "$set" => {:active_quests => player.active_quests.to_mongo} )
return_val = query.update( {:_id => params[:player_id]}, {"$set" => {:active_quests => active_quests_list.to_mongo}})
I'm hoping someone here might know what I am doing wrong.
After further investigation, it turns out that this was a problem relating to how the player variable was being updated outside of the function.
The following lines will update the record in this case (both in-memory and in the database)
player[:active_quests] << #active_quests_list
player.push_all(:active_quests => player.active_quests)
However, the player variable was local to this function in this case, and was being updated again after the function returned.
This was only discovered after careful examination of the output of "mongod -vvvvv".
Related
So I set up a postgres server and have it working with hstore values.
As of right now, I have a table books, structured with
name:string data:hstore
I have created a sample entry to test:
Book.create(:name => "My First Book", :data => {'author' => 'Kevin', 'pages' => 368})
I have loaded the data into a variable:
#book = Book.where("data ? :key", :key => 'pages')
(just to test, i realize this query would serve no real purpose...)
I print the data as JSON and this works fine, the entry is found and displayed. However, what I am trying to do is access, say the pages, an hstore value. I did some research and found
#book.data['pages']
However, when i try to run this, I get
undefined method `data' for #<Book::ActiveRecord....
Any and all help is greatly appreciated!
The Active Record where will give you an array even if there is only 1 value.
You can do
#book = Book.where("data ? :key", :key => 'pages')[0]
to get that record
and then
#book.data
will work as desired.
If you might get multiple records and just using the first found is ok you could also use:
#book = Book.where("data ? :key", :key => 'pages').first
#book.data
or just
#book = Book.where("data ? :key", :key => 'pages').first.data
After fiddling around, i found that I simply needed to call:
#book[0].data
host = Host.find(i)
a = host.open_ports
openPorts = []
a.split(",").each do |x|
openPorts << x
end
This is the set up were talking Ruby on Rails, so I set up my Ip address on iand grab all the open ports.String returned is then broken up via "," and added to an array.
Finding.all.each do |p|
openPorts.each do |y|
if p.port == y
Report.create(:port => p.port,
:risk_rating => p.risk_rating,
:finding => p.finding,
:implication => p.implication,
:recommendation => p.recommendation)
end
end
end
Iterates through findings table in the database and checks if the ports match the open ports array we created above. If there is a match we create a new report, based on the given value from the finding table.
The problem is does not create a new report even if there is a match.
Any help is appreciated.
Not sure if this helps but I wanted to show you how to clean up the implementation a bit.
host = Host.find(i)
# I am assuming Finding#port is an Integer
# if Finding#port is a String then just remove the .map(&:to_i) portion
open_ports = host.open_ports.split(",").map(&:to_i)
Finding.where(port: open_ports).each do |p|
Report.create(:port => p.port,
:risk_rating => p.risk_rating,
:finding => p.finding,
:implication => p.implication,
:recommendation => p.recommendation)
end
Lets start at the top
String#split returns an Array so no need to push it into a new one. It does however create an Array of Strings so if you need integers #map(&:to_i) will do this for you. I am assuming this is the current issue which is comparison of a string with integer for example "80" == 80 #=> false
Next rather than loop through all the Findings why not just pull out the one's with matching ports? Finding.where(port: open_ports) this will generate a query like SELECT findings.* FROM findings where findings.port IN (YOUR_OPEN_PORTS_ARRAY)
Then we just create the reports from this limited list instead of the loop through all Findings and then a loop through open_ports as well.
Currently on save I am trying to check to see if a recorded falls into a particular 'scope'. This 'scope' really is just some saved arguments for a .where call. Also with this 'scope' I am only ever checking values of the object, not ever how it relates to other objects in the database, so querying the database will always be over kill if that makes sense.
I have only been able to come up with the below solution
begin
result = self.class.where(scope).find(self.id)
rescue
result = false
end
The issue with this is that I have to query the database even though I already have the record, and I have to run this not only before save but after save to check the values it was and the values it will be after save, because there is no way to query the database for the updated version if it hasn't been saved.
There can be a number of these checks so I would like to avoid having to do it twice, and also having to query the database that many times, even if ultimately I am just looking something up by id.
The only other solution I have been able to think of would be to have a method that some how translates the where call into a proc that return a boolean when passed an object. The only issue with that is translating it would some how have to work with the active record adapter being used, which seems like a whole project to its own. So does anyone know of some way to do this, or of a gem that would help?
PS I getting the 'scope' from cache so I can't save it as a proc because you can't put procs into the cache with Rails.
first you can improve your first solution a bit
result = self.class.where(scope).exists?(self.id)
if you don't want to check the database, why don't you just check if your object's attributes has the values of the scope? if your scope is
class.where(:attr1 => value1, :attr2 => value2, :attr3 => value3)
then you can do
result = self.attr1 == value1 and self.attr2 == value2 and self.attr3 == value3
If your scopes are simple, you probably want to avoid code duplication. My solution allows you to call model.active? to know if an instance belongs to the scope, and Model.active to find all records matching the scope. model.active? doesn't involve any database queries.
consider adding this to config/initializers/scope_and_method.rb:
require 'active_record/named_scope'
module ActiveRecord::NamedScope::ClassMethods
def scope_and_method field, *values
field = field.to_sym
values.each do |value|
named_scope value.to_sym, :conditions => {field => value}
define_method "#{value}?" do
send(field.to_sym) == value
end
end
end
end
Usage:
scope_and_method :state, 'active', 'inactive'
Works as if it was:
named_scope :active, :conditions => {:state => 'active'}
named_scope :inactive, :conditions => {:state => 'inactive'}
def active?
state == 'active'
end
def inactive?
state == 'inactive'
end
This is a solution for Rails 2.3. This needs a very small tuning for Rails 3 and 4. (named_scope -> scope) I will check it soon.
In my view page, i am using form_tag to create a form which will pass a string of ids from a hidden field to the controller code.
In my controller code, i am looping through an array of ids to update each record containing that id in the Expression table. But the code below does not seem to work.
I would really appreciate it if somebody could give me some suggestion regarding what is wrong with the code below.
def update_expression
#emi_ids_array = params[:emi_ids].split(/,/)
#sub_id = params[:sub_id]
#emi_ids_array.each do |emi_id|
#existing_exp = Expression.find(:first, :conditions => [ "EXT_EMI_ID = ? and EXT_SUB_FK = ?", emi_id, #sub_id])
#expression = #existing_exp.update_attributes(
:EXT_SUB_FK => #sub_id,
:EXT_PRESENCE => "present",
:EXT_STRENGTH => "weak",
:EXT_EMI_ID => emi_id
)
end
end
Try converting the array of ID's (and the sub_id) to integers.
Is it the finding of the object that fails, or the update? Output the #expression.errors after the update call to see if there are any validations failing.
Is there a reason for all the instance variables? You don't need the #'s if the variable doesn't go beyond that method. Also the #expression item seems superfluous, you're just duplicating the #existing_exp object, you don't need to put the return into a new object, especially if it's replaced each time the loop runs anyway.
Found a temporary solution. 'update_attributes' does not seem to work, so i opted for 'update_all' attribute
Expression.update_all({:EXT_PRESENCE => "present", :EXT_STRENGTH => "weak"},['EXT_EMI_ID = ? and EXT_SUB_FK = ?', emi_id, #sub_id])
Hopefully, it might be useful to someone else
This is a snippet of code from an update method in my application. The method is POSTed an array of user id's in params[:assigned_ users_ list_ id]
The idea is to synchronise the DB associations entries with the ones that were just submitted, by removing the right ones (those that exist in the DB but not the list) and adding the right ones (vise-versa).
#list_assigned_users = User.find(:all, :conditions => { :id => params[:assigned_users_list_id]})
#assigned_users_to_remove = #task.assigned_users - #list_assigned_users
#assigned_users_to_add = #list_assigned_users - #task.assigned_users
#assigned_users_to_add.each do |user|
unless #task.assigned_users.include?(user)
#task.assigned_users << user
end
end
#assigned_users_to_remove.each do |user|
if #task.assigned_users.include?(user)
#task.assigned_users.delete user
end
end
It works - great!
My first questions is, are those 'if' and 'unless' statements totally redundant, or is it prudent to leave them in place?
My next question is, I want to repeat this exact code immediately after this, but with 'subscribed' in place of 'assigned'... To achieve this I just did a find & replace in my text editor, leaving me with almost this code in my app twice. That's hardly in keeping with the DRY principal!
Just to be clear, every instance of the letters 'assigned' becomes 'subscribed'. It is passed params[:subscribed_ users_ list_ id], and uses #task.subscribed_ users.delete user etc...
How can I repeat this code without repeating it?
Thanks as usual
You don't need if and unless statements.
As for the repetition you can make array of hashes representing what you need.
Like this:
[
{ :where_clause => params[:assigned_users_list_id], :user_list => #task.assigned_users} ,
{ :where_clause => params[:subscribed_users_list_id], :user_list => #task.subscribed_users}
] each do |list|
#list_users = User.find(:all, :conditions => { :id => list[:where_clause] })
#users_to_remove = list[:user_list] - #list_users
#users_to_add = #list_users - list[:user_list]
#users_to_add.each do |user|
list[:user_list] << user
end
#users_to_remove.each do |user|
list[:user_list].delete user
end
end
My variable names are not the happiest choice so you can change them to improve readability.
I seem to be missing something here, but aren't you just doing this?
#task.assigned_users = User.find(params[:assigned_users_list_id])