Neo4j gem - Updating relationship properties method - ruby-on-rails

This is a fairly simple question.
Is there a specific method for updating properties for relationships in the gem? I can't seem to do a simple update_attributes or update.
My relationship is created and update is undefined for nil class.
event = Event.find(params[:event])
invite = Invite.create_invitation(event, current_user, nil) #creates relationship
invite.update(interested: params[:interest])
Is there another method that I should be using?

Whenever you get an error about undefined for nil class, it should be an immediate signal to you that the problem is the variable on which you're calling the method, not the method you're calling. In other words, since it's saying that nil does not have an update method, your focus should be on the variable that is actually nil. The question here: why is invite returning nil instead of a relationship?
Since you're using a custom class method to create the relationship, I'm guessing that you're not telling it to return the right object. It should look something like this:
def self.create_invitation(event, user, something_else = nil)
rel = InvitationRel.new(from_node: event, to_node: user)
if rel.save
rel
else
# return an error, maybe rel.errors.full_messages?
end
end
Then your controller needs to check that the rel was actually created correctly.
event = Event.find(params[:event])
invite = Invite.create_invitation(event, current_user, nil) #creates relationship
if invite.neo_id.nil?
# the rel did not create correctly, neo_id is only present on persisted objects
else
invite.update(interested: params[:interest])
end
This feels to me like you're taking a long way around this problem. You don't need to separate the creation of the relationship and setting the interested property, you can just do it in one call to the DB:
event = Event.find(params[:event])
invite = InviteRel.new(from_node: event, to_node: current_user, interested: params[:interest])
if invite.save?
# rel was created, move on
else
# something wrong in the rel, figure out what it was
end
Since you know you're always going to create that interested property, this looks like a good place to add a validation to your model to ensure that the property is always set on create.

Related

Updating if exist or create if not rails

So im using an api to get info on weather, its executes everyday, what im trying to do is to get updated if already exist, and create a new one if it doesn't in table.
I do want to update all attributs when udpdating.
i did try
model = Model.where(column_name: value).first_or_initialize(locked: false)
but i get an error saying :
unknown attribute locked for Model
raise UnknownAttributeError.new(self ,k.to_s)
If you need anything, ask and i will comment or edit. Im newb to ruby and rails
Firstly, the model.Model part should be just Model, as Model is your class.
locked is supposed to be a column/attribute of the Model class, although it seems is not the case judging from your error. Therefore, I'm gonna use other_column_name as an example.
Explanation of what this is doing:
Model.where(column_name: value).first_or_initialize(other_column_name: some_value)
Model.where(column_name: value): gets models that satisfy the condition column_name == value
first_or_initialize: if a model such that column_name == value was found, that one is returned. Otherwise, it initializes a model with column_name = value.
By passing other_column_name: some_value, if the model was not found and needs to be initialized, it sets other_column_name to some_value but: 1) it does not update it if it was initially found and 2) it does not save the record.
The equivalent of first_or_initialize that saves the new record would be first_or_create but this would still not update the record if it already existed.
So, you should do something like this:
m = Model.where(column_name: value).first_or_initialize
m.other_column_name = some_value
m.save
This way, you first get a model where column_name is value or initialize a new one with this value if it didn't already exist. Then, you set the attribute other_column_name to some_value and save the model.
A one-liner alternative would be
Model.where(column_name: value).first_or_create.update(other_column_name: some_value)
However, note that if it needs to be created, this one will perform 2 queries (the insert and the update).
About the error part. It says the attribute locked does not exist on the Model record. Are these classes you created? Are you using some pre-existing project? You could try posting Model.attribute_names and maybe your schema.rb
Firstly refer to the docs here
A table by the name of weather with the following attributes location: string temperature:integer wind:string needing to be updated or initialized based on the location would work like this
#weather_record = Weather.find_or_initialize_by(location: location_value)
#weather.temperature = -60
#weather.wind = strong
#weather.save
Next, never, ever use a reserved name for a model so do not have Model as the name of your table
Lastly in your example
model.Model.where(column_name: value).first_or_initialize(locked: false)
you are saying
a_record.ClassName.where which is just wrong, If you are using a class method then start with the class name e.g. Weather.where if you are using instance methods then use the instance name e.g. an_instance_of_weather.some_field
Try this mate:
column_name_value = (Way that you get the info from data)
model = Model.find_or_initialize_by column_name: column_name_value
Let me know if worked!

Extra attribute in many to many join table

I have a many to many association between User and Todo through a join model called UserTodo
Of the many users a todo has, there's one owner. So, I created a column in the user_todos join table called is_owner.
Question is, how do I populate this attribute while creating a Todo?
Currently, I'm creating the todo, then separately updating this attribute in TodoController#create action.
#todo = current_user.todos.create(todo_params)
#todo.user_todos.first.update_attribute(:is_owner, true)
This seems wrong. Is there a single call I can make to populate this attribute while creating the todo?
Second, is there a way to query if a user is an owner of a todo, this way?
current_user.todos.first.is_owner?
I would make a user_todo.rb file with a UserTodo class and do stuff like:
ut=UserTodo.new
ut.todo = Todo.create(todo_params)
ut.user = current_user
ut.is_owner = true
ut.save
current_user.todos_as_usertodos.first.is_owner?
You can make on user.rb
def todos_as_usertodos
UserTodo.where(user_id: id).to_a
end
See where I'm going with this? You want to return and use UserTodo objects vs. Todo objects because they have more info in them. The info you need. That extra is_owner boolean. When you goto just a plain todo object you lose that info.
That seems a bad way since you use .first to get your instance. I'll do something like
UserTodo.create(user: current_user, is_owner: true, todo: Todo.create(todo_params))
Second
I'm not sure if that is possible
You can use this one-liner to create the join model with extra attributes:
current_user.user_todos.create(todo: Todo.create(todo_params), is_owner: true)
Since is_owner is an attribute of the join model, you have to access it through that model too:
current_user.user_todos.first.is_owner?

How to update table row from values determined within controller

ruby_on_rails rails 4 assignment non-screen data to insert record
Rather than using screen values (e.g. simple_form_for #user_evaluation_result) to populate the columns to insert a row I need to calculate some of the values in controller.
For example if I have these statements in the controller
….
# which if I had simple_form_for user_evaluation_result would get populated by the screen
#user_evaluation_result = UserEvaluationResult.new(user_evaluation_result_params)
….
# standard stuff I use for screen derived updates
def user_evaluation_result_params
params.require(:user_evaluation_result).
permit(:evaluation_assumption_id,
:company_listing_id,
:target_share_price_dollars )
end
How do I assign values to :user_assumption_id etc so that insert works. I have tried all sorts of statements. Alternatively do I use another format instead of calling "def user_evaluation_result_params".
Thanks in advance - Pierre
I'm hoping I've interpreted the question properly...
First, to make sure we're on the same page... The code inside of your user_evaluation_result_params method is using Strong Parameters to create an ActionController::Parameters object for the purpose of protecting your model from unpermitted mass-assignments. So, in general, when you're creating or updating an ActiveRecord object from a form in a view template, you want to use Strong Parameters so that users can't manipulate your form to set attributes that you're not expecting.
That said, if you want to set attributes on an object you don't have to use mass assignment. Here is an example of using one-at-a-time assignment (the opposite of mass-assignment):
obj = MyObject.new
obj.attr_one = "One"
obj.attr_two = "Two"
obj.save
There is nothing wrong with this approach other than that it's kind of a lot of work for the general case. So mass-assignment just saves us from having to do this all the time. But it sounds like this one-at-a-time assignment is what you're wanting in this case. So try something like this:
def create
#user_evaluation_result = UserEvaluationResult.new
# assuming you have a UserAssumption object instance in `my_user_assumption`
#user_evaluation_result.user_assumption = my_user_assumption
#user_evaluation_result.some_other_attr = "some value"
#user_evaluation_result.save
end
Note, instead of setting #user_evaluation_result.user_assumption_id directly, as you asked about, it is preferred to set the actual object association as I did above. Try to keep associations outside of mass-assignment and use object relationships to build up your object graphs.
Or, if you have some attributes coming from a form you can mix and match the two approaches:
def create
#user_evaluation_result = UserEvaluationResult.new(user_evaluation_result_params)
# assuming you have a UserAssumption object instance in `my_user_assumption`
#user_evaluation_result.user_assumption = my_user_assumption
#user_evaluation_result.some_other_attr = params[:user_evaluation_result][:some_other_attr]
#user_evaluation_result.save
end
private
def user_evaluation_result_params
params.require(:user_evaluation_result)
.permit(:evaluation_assumption_id,
:company_listing_id,
:target_share_price_dollars)
end

Can one easily stub methods on all instances of a specific Activerecord?

Say I want to make a test for a method that retrieves records. For one of the records I'd like record.remote to be stubbed to return a certain object and for others to return some other object. Class.any_instance comes close to what I want, but I'd like to be able to filter the instances down to those coming from a specific record.
Something like this would be OK if it would work.
Answer.any_instance.stub(:remote).and_return do
if self.id == #answer_2.id
remote_answer
else
remote_complete_answer
end
end
Except that self is not an Answer in this case but an RSpec::Core::ExampleGroup. Can I get to the original object in an and_return block?

Trouble on finding a class object in a array of classes

I am using Ruby on Rails 3.0.7 and I would like to understand how to handle the following code in order to retrieve a class objects with a specified id.
In my view file I have:
#records = Users.all # This returns an array (class)
In another file, a partial template, I would like to retrieve, for example, the user with id 1, but if I make this:
#records.find(1)
I get an enumerator (class) of all records:
<Enumerator: [<Users id: 1, ... ] >
How can I find the user with id 1 (or other ids) "a là Ruby on Rails Way"?
UPDATE
I use #records = Users.all in a view file because I aim to minimize calls to the database since I need to iterate almost over all records and check them existence. If I do for example:
some_hash.each { |key, value|
put User.find(value)
}
and I go in the log file, I will see a lot of database requests.
Even though this is probably quite slow, and I suspect there are some less than optimal designs in the app you're working on (not judging, we've all been there), Array#index seems to be what you're looking for:
#records[#records.index{|user| user.id == 1}]
Edit
Although if you need to do something for every user, and you need to access them by id quickly, I'd probably do something like this in your controller. Even if it's not really faster, it's much more readable (to me anyways):
#users_hash = {}
User.all.each{|user| #users_hash[user.id] = user}
Then in your views you can do:
#users_hash[id].username
Use User.scoped instead of User.all. #all will immediately query the database and return an array, whereas #scoped will return an ActiveRecord::Relation object which you can chain further queries. In this case, the database won't be hit until you try and somehow inspect or enumerate the result
Actually you're mistaken. #records.find(1) is returning an object of the class Enumerator (which is not the same as the class Enumerator itself).
The problem here is that, as you've noted, #records is an Array, not an ActiveRecord object, and Array#find (inherited from Enumerable#find--which, when not given a block, returns an object of class Enumerable) is not the same method as ActiveRecord::Base#find (i.e. User#find).
What you should do is, in your controller, pick out the one user record you want:
#user = User.find 1
...and then use #user directly in your template. Generally you should avoid doing ActiveRecord lookups (e.g. find) in your templates. That kind of logic should happen in your controller.
Last time for such case I ended up doing like this:
#assignments = Assignment.find_by_sql(' ... ')
#assignments.find(id: 1).first

Resources