Ruby query need to update - ruby

Relationship:
account has_many users
user has_one sima
primary_partner_id is "account_id" which is passing as params.
User.where(primary_partner_id: 2).map{|a| a.sima}.reject{ |e| e.to_s.empty?}
Results as below:
[
#<Sima id: 93, user_id: 7, interviewer_account_user_id: 1945, interviewer_completion_date: "2017-06-09", transcriber_account_user_id: nil, transcriber_completion_date: nil, biographer_account_user_id: nil, biographer_completion_date: nil, reviewer_account_user_id: nil, reviewer_completion_date: nil, status: "accepted", autobiographical_form: "27381", autobiographical_form_completion_date: nil, sima_level_id: "1", created_at: "2017-06-06 20:17:57", updated_at: "2017-06-09 10:04:33", autobiographical_form_comments: nil, on_hold: nil, comments: [{:comment=>"easylims.xlsx", :user_name=>"Mike Burns", :created_at=>2017-06-06 20:17:57 UTC}, {:comment=>"ok", :user_name=>"SIMA Admin", :created_at=>2017-06-06 20:19:33 UTC}], interviewer_id: nil, interviewer_start_date: nil, transcriber_start_date: nil, biographer_start_date: nil, reviewer_start_date: nil>,
#<Sima id: 92, user_id: 1, interviewer_account_user_id: nil, interviewer_completion_date: nil, transcriber_account_user_id: nil, transcriber_completion_date: nil, biographer_account_user_id: nil, biographer_completion_date: nil, reviewer_account_user_id: nil, reviewer_completion_date: nil, status: "accepted", autobiographical_form: "27437", autobiographical_form_completion_date: nil, sima_level_id: "1", created_at: "2017-06-06 20:01:50", updated_at: "2017-06-06 20:22:50", autobiographical_form_comments: nil, on_hold: nil, comments: [{:comment=>"original_msg (1).txt", :user_name=>"bild_cloud#bild.org", :created_at=>2017-06-06 20:01:50 UTC}, {:comment=>"ok", :user_name=>"SIMA Admin", :created_at=>2017-06-06 20:22:05 UTC}], interviewer_id: nil, interviewer_start_date: nil, transcriber_start_date: nil, biographer_start_date: nil, reviewer_start_date: nil>
]
Sima has field like status and I have three statuses as "pending, accepted, declined"
Question:
Now I want to show the result as per status which will pass by the user as params. So the result should be as per status from Sima. If params is status=" accepted" then Sima list will have only which has status "accepted"

By doing this you can avoid N+1 Query problem
User.includes(:sima).where(sima: {status: params[:status]},primary_partner_id: params[:account_id]).map(&:sima)
If you are using joins --> In map statement each time query will be called so that we are preferred to use includes, for more Information
you can refer this link
http://guides.rubyonrails.org/active_record_querying.html

You could join the simas model and the user model
User.joins("JOIN simas ON simas.user_id = users.id")
.where(primary_partner_id: 2, "simas.status": 'accepted')
.map{|a| a.sima}

As I am understanding your above all statements. End user us passing account id and you want to take sima status from the end user as well.
You can file all users id from account first. then find sima which will belong to that user and here you can pass sima status as well.
#aBadAssCowboy given the correct answer which is first one only. But from that query, you will get all Sima either nil or with value.
user_ids = User.where(primary_partner_id: params[:account_id]).pluck(:id)
Sima.where(user_id: user_ids, status: params[:status]).reject{ |e| e.to_s.empty?}
Hope this will help you.

User.preload(:sima).joins(:sima).where(sima: {status: params[:status]}, primary_partner_id: params[:account_id]).map(&:sima)
or
As account to users relationship is already defined you can directly do...
Account.find(2).users.preload(:sima).joins(:sima).where(sima: {status: params[:status]}).map(&:sima)

With the same associations you have on the models, you could do (assuming that user passes status they wants to see:
user_ids = User.where(primary_partner_id: 2).pluck(:id)
Sima.where(user_id: user_ids, status: params[:status])
The above will hit the database twice but that's totally fine. You could use includes and map but I don't think that will be a huge performance benefit depending on the number of users on a account you have. Eitherways, here it is:
User.includes(:sima)
.references(:sima) # you need this if you use Rails 4 +
.where(users: { primary_partner_id: 2 })
.where(simas: { status: params[:status] })
.map(&:sima)
However, I suggest you update Account model with has_many through associations.
class Account
has_many :users
has_many :simas, through: :users
end
# To get sima of a particular status
Account.find(2).simas.where(status: param[:status])
This way, you can just do account.simas to get simas from all the users that belong to that account.

If you add to Sima Model
belongs_to :user You can get an list of Sima by
Sima.joins(:users).where(users: { primary_partner_id: 2 }).where(status: 'accepted')

Related

Initializer doesn't create instance in console

I am new to ruby and I am trying to create a hangman game, to do so I need to create a new game each time the user click on a button.
First step is to create the same object each time the methode create will be called (it will be different because of the .sample.
I am trying to create an object with the initialize method in my model.rbfile.
Here is my code :
class Game < ApplicationRecord
has_many :guesses
def initialize(*)
super
#word_to_guess = word_to_guess
#health_bar = 5
#game_status = game_status
end
def word_to_guess
words = [
"spokesperson", "firefighter", "headquarters", "confession", "difficulty", "attachment", "mechanical",
"accumulation", "hypothesis", "systematic", "attraction", "distribute", "dependence", "environment",
"jurisdiction", "demonstrator", "constitution", "constraint", "consumption", "presidency", "incredible",
"miscarriage", "foundation", "photography", "constituency", "experienced", "background", "obligation",
"diplomatic", "discrimination", "entertainment", "grandmother", "girlfriend", "conversation", "convulsion",
"constellation", "leadership", "insistence", "projection", "transparent", "researcher", "reasonable","continental",
"excavation", "opposition", "interactive", "pedestrian", "announcement", "charismatic", "strikebreaker",
"resolution", "professional", "commemorate", "disability", "collection", "cooperation", "embarrassment",
"contradiction", "unpleasant", "retirement", "conscience", "satisfaction", "acquaintance", "expression",
"difference", "unfortunate", "accountant", "information", "fastidious", "conglomerate", "shareholder",
"accessible", "advertising", "battlefield", "laboratory", "manufacturer", "acquisition", "operational",
"expenditure", "fashionable", "allocation", "complication", "censorship", "population", "withdrawal",
"sensitivity", "exaggerate", "transmission", "philosophy", "memorandum", "superintendent", "responsibility",
"extraterrestrial", "hypothesize", "ghostwriter", "representative", "rehabilitation", "disappointment",
"understanding", "supplementary", "preoccupation"
]
words.sample
end
def game_status
game_status = ["Game not started yet", "In Game", "You win", "You loose"]
game_status[0]
end
end
The problem is that when I do a Game.new in console, it returns :
[1] pry(main)> Game.new
=> #<Game:0x00007fa79b613290 id: nil, word_to_guess: nil, health_bar: nil, created_at: nil, updated_at: nil, guesse_id: nil, game_status: nil>
So the instance is not created and I don't know why.
As you see, instance was created
But I don't recommend to use instance variables such way. Instance variables are stored in memory
Also it's not good idea to override "getters" for rails attributes such way
It's better to store such data in database as rails attributes
You can do like this
after_initialize do
self.word_to_guess ||= generate_word_to_guess
self.health_bar ||= 5
self.game_status ||= "Game not started yet"
end
Also it looks that for health_bar and game_status is better to define default values in database
You can read more about callbacks:
https://guides.rubyonrails.org/active_record_callbacks.html

Sinatra activerecord initialize

I got a strange situation.Could somebody give me advise ?
I am developing an App with
Ruby1.8.7
Sinatra 1.4.4
Activerecord 3.2.14
Mysql 5.6.19
I almost finish developing but at the last moment I got stack.
I have two tables in MySQL.
CREATE TABLE items(
id INT PRIMARY KEY AUTO_INCREMENT NOT NULL,
type text,
keyword text,
postid INT,
created_at datetime NOT NULL);
CREATE TABLE comments(
id INT PRIMARY KEY AUTO_INCREMENT NOT NULL,
comment text,
yourname INT,
postid INT,
created_at datetime NOT
NULL);
In Sinatra app , I declared class.
class Comment < ActiveRecord::Base end
class Item < ActiveRecord::Base end
For debugging purpose , I wrote this code and run.
get "/l" do
# New comment and set initial value.
y={:yourname =>"3",:comment =>"commenttest"}
com = Comment.new(y)
p com.attribute_names()
p com
# New items and set initial value.
kensaku = {:type=>"000"}
k = Item.new(kensaku)
p k.attribute_names()
p k
k.type="555"
p k
end
So now I found very interesting things on console.
Comment class succeeded to new with initial value.
But Item class succeeded to new but it is not set initial value.
I wonder why it happens ?
#-- Comment class
["postid", "id", "comment", "created_at", "yourname"]
#<Comment id: nil, comment: "commenttest", yourname: 3, postid: nil, created_at: nil>
#-- Item class
["type", "postid", "id", "keyword", "created_at"]
#<Item id: nil, type: nil, keyword: nil, postid: nil, created_at: nil>
#<Item id: nil, type: "555", keyword: nil, postid: nil, created_at: nil>
The column name type is a reserved word in ActiveRecord:
While these column names are optional, they are in fact reserved by
Active Record. Steer clear of reserved keywords unless you want the
extra functionality. For example, type is a reserved keyword used to
designate a table using Single Table Inheritance (STI). If you are not
using STI, try an analogous keyword like "context", that may still
accurately describe the data you are modeling.

Rails 4.0.3: after_save callback seems to be called too soon?

Given the following two classes Customer and Order, I have a problem which I have narrowed down to the execution times of the callbacks I am using.
The reason for the use of callbacks is that I need to store the aggregated diff of each customer without having to iterate through all of his/her orders each time. So I realized that it might be a good approach to use callbacks to first update the diff on each individual order before each save, and second, update the aggregated diff on the customer model after each order is saved.
The symptoms I get are weird seemingly random errors where it tells me that the diff between two orders which both totals $238 is $15. When I limited the debugger to the affected customers I realized that it was simply skipping the orders.each loop below (but only in some cases) because there were no orders associated to the customer.
Since all Customers have at least one order and the order is actually created before the customer in this system, that can't be true.
Is there some way I can enforce that the after_save callback is indeed not called until ActiveRecord has finished both saving the Order object and updating the relation to the Customer model?
As you can see I also tried to use a timer to see if I could get to work by sleeping the thread for a short while before continuing, but that did not work.
class Customer < ActiveRecord::Base
...
has_many :orders
before_save :calculate_shipping
def update_total_diff
sum = 0
orders.each do |o|
if o.diff.present?
sum += o.diff
end
end
# HERE IS THE PROBLEM: When inspecting the orders collection in the debuggger it is empty.
# Make sure shipping is updated
calculate_shipping
# Since we are calculating the diff shipping must be subtracted
sum -= shipping
self.total_diff = sum
self.save
end
...
private
def calculate_shipping
...returns either 15 or 0
end
end
And the Order model:
class Order < ActiveRecord::Base
...
belongs_to :customer
before_save :update_diff
after_save :update_customer
private
def update_diff
#Updates a local value
......
end
def update_customer
#Here we trigger a function on the customer model in order to tell it that it needs to re-calculate
sleep(1/10) #Tried this to test - no luck
if self.customer_id.present? && diff.present?
self.customer.update_total_diff
end
end
end
Edit:
Have a look at this:
From: /vagrant/app/models/order.rb # line 23 Order#update_customer:
21: def update_customer
22: if self.customer_id.present?
=> 23: binding.pry
24: end
25: if self.customer_id.present? && diff.present?
26: self.customer.update_total_diff
27: end
28: end
...
[4] pry(#<Order>)> michael = self.customer
=> #<Customer id: 2, name: "Michael xxx", mail: "xxx#gmail.com", shipping_address_id: 2, created_at: "2014-07-17 11:00:12", updated_at: "2014-07-17 11:00:12", shipping: #<BigDecimal:7fada4e707d0,'0.0',9(36)>, total_diff: nil>
[5] pry(#<Order>)> michael.orders
=> []
[6] pry(#<Order>)> michael = Customer.find(2)
=> #<Customer id: 2, name: "Michael xxx", mail: "xxx#gmail.com", shipping_address_id: 2, created_at: "2014-07-17 11:00:12", updated_at: "2014-07-17 11:00:12", shipping: #<BigDecimal:7fada5008098,'0.0',9(36)>, total_diff: nil>
[7] pry(#<Order>)> michael.orders
=> [#<Order id: 2, pledge_id: 4808304, amount: #<BigDecimal:7fada50fc120,'0.89E2',9(36)>, customer_id: 2, perk_id: 9, created_at: "2013-11-23 00:00:00", updated_at: "2014-07-17 11:00:12", diff: #<BigDecimal:7fada5108240,'0.0',9(36)>>]
[8] pry(#<Order>)>
I tried stopping my debugger right before the Order tells Customer to re-calculate. I tried assigning the same customer twice to the variable "michael". If I assign the variable simply with:
michael = self.customer
The object won't know about any of its associated orders. If I assign it thorugh activerecord like so:
michael = Customer.find(2)
It will work. Why is that?
For now I managed to fix it by doing this, but I'd still like to know if anyone can explain the situation for me?
me = Customer.find(id)
me.orders.each do |o|
if o.diff.present?
sum += o.diff
end
end

CollectionProxy object in rails 4?

I am using rails 4 and making an application in which Question_sets has many Questions and Questions has many answers. Now what i want is to update one attribute of question model and one attribute of answer model through questions sets.
#question_set.questions.inspect
It gives the output
OUTPUT:
ActiveRecord::Associations::CollectionProxy
[Question id: nil, title: "werewr", question_type: "Slider", description: "ewrewrew", is_shown_in_report: false, question_set_id: nil, user_id: nil, created_at: nil, updated_at: nil]
But when i am accessing any of the attribute or trying to update it, it will give an error that is undefined "attribute_name".
can any tell how to access the attributes of the this collectionproxy object.
questions is returning a collection (like an Array) of Question objects. So you need to itterate over the collection:
#question_set.questions.each do |question|
question.title
end

Rails 3- find_by not working

I have a strange problem.
I have two models Users and roles with a many to many relationships between them.
I want to filter the roles with name 'Administrator' within the role collection of a user.
In the model this code
puts self.roles.to_s
Prints to screen:
[Role id: 1, name: "Administrator", created_at: "2012-01-22 21:55:45", updated_at: "2012-01-22 21:55:45"]
But this code
puts self.roles.find_by_name('Administrator').to_s
doesn't print anything. And this one:
puts self.roles.find_by_name('Administrator').nil?
prints true!
Why isn't my find_by_name method not working?
I tryied it in the console and it works well.
My code snippet is the following:
puts self.roles.to_s
puts self.roles.find_by_name('Administrator').to_s
puts self.roles.find_by_name('Administrator').nil?
And the output is the following:
[Role id: 1, name: "Administrator", created_at: "2012-01-22 21:55:45", updated_at: "2012-01-22 21:55:45"]
<none>
true
What am I doing wrong?? It has to be something stupid.
This code is located in a validate method, so it is executed before the user is saved.
You mentioned that you are doing this in a validation before the model is saved. This is why it's failing. Since the User is not yet saved, it doesn't have an id in your database, so there's no way for it to search your associations.
The reason the first line works (self.roles.to_s) is that Rails memorizes any roles you add to your user, but does not save them to the database until you save the user. Once you save the user, your second/third lines would work fine:
user = User.new
user.roles << Role.first
user.roles
# => [#<Role id: 1, name: "Administrator" ...>]
user.roles.find_by_name("Administrator")
# => nil
user.save # This inserts both the new user AND the associations
user.roles.find_by_name("Administrator")
# => #<Role id: 1, name: "Administrator" ...>
If you have to work with this in your validations, you might try using Enumerable's find method to search the roles array instead:
user = User.new
user.roles << Role.first
user.roles
# => [#<Role id: 1, name: "Administrator" ...>]
user.roles.find { |role| role.name == "Administrator" }
# => #<Role id: 1, name: "Administrator" ...>
user.roles.find { |role| role.name == "Foo" }
# => nil

Resources