Sinatra activerecord initialize - ruby

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.

Related

Error while passing argument list to Superclass

Sorry if the question is unclear as I'm new to Ruby.
I have a Parent class (not implemented by me) Reports that has a method query defined as:
class Reports
def self.query(id:, is_active: false, is_timestamp: false, version: DEFAULT_VERSION, limit: DEFAULT_LIMIT)
<<code>>
end
I have defined a child class Report that inherits Reports and defined the same method query as below:
class Report < Reports
def self.q_version(id)
<<some logic to decide version>>
end
def self.query(id:, is_active: false, is_timestamp: false, limit: DEFAULT_LIMIT)
version = q_version(id)
super(id, is_active, is_timestamp, version, limit)
end
Now when I run the code, I'm getting an argument error as such:
ArgumentError: wrong number of arguments (given 5, expected 0; required keyword: id)
I suspect I'm not doing the super call correctly but couldn't figure out which part.
so any help would be appreciated.
The method's signature tells that it expects keyword arguments like this:
def self.query(id:, is_active: false, is_timestamp: false, limit: DEFAULT_LIMIT)
version = q_version(id)
super(id: id, is_active: is_active, is_timestamp: is_timestamp, version: version, limit: limit)
end
Or you could use the new shorthand hash syntax when you are on Ruby 3.1+:
def self.query(id:, is_active: false, is_timestamp: false, limit: DEFAULT_LIMIT)
version = q_version(id)
super(id:, is_active:, is_timestamp:, version:, limit:)
end

Ruby query need to update

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')

Mongoid push with upsert

I've got model User:
class User
field :username, type: String
embeds_many :products
end
class Product
field :name, type: String
embedded_in :user
end
I would like to have single operation that would:
insert the user
update the user in case the user exists already (this i can easily do with upsert)
push the products
This works for upserting:
User.new(username: 'Hello').upsert
The problem is that this will delete the embedded products (the products attribute is not specified).
Can I ask mongoid to skip setting array to empty?
Can I ask mongoid to push new products at the end of products array?
Something like this:
User.new(username: 'Hello').push(products: [Product.new(name: 'Screen')]).upsert
Finally I ended up by manually writing the following query:
User.mongo_client[:users].update_one({username: 'Hello'},
{"$set" => {first_name: 'Jim', last_name: 'Jones'},
"$pushAll" => [products: [{name: 'Screen'}, {name: 'Keyboard'}]
},
upsert: true)
Where:
$set - are the params that we want to set for a given document
$pushAll - when you use $push you can specify only one element, $pushAll allows you to append multiple elements (when you specify only one it will behave like $push)
upsert - will do the insert/update magic in the mongodb
In the second hash you can also specify $inc, $dec, $pop, $set etc... which is quite useful.

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

Resources