Using ActiveRecord Enum in array selections - ruby-on-rails

I have a model
class Order < ActiveRecord::Base
...
enum status: [:started, :finished, :failed, :processing]
...
end
And want to select all processing and finished orders. I cannot write simply
#orders = Order.where(status: [:finished, :processing])
because status field is naturally an integer, not string or literal. So this statement generates
2.2.0 :008 > Order.where(status: [:finished, :processing])
SELECT "orders".* FROM "orders" WHERE "orders"."status" IN (NULL, NULL)
Now i do the following
#orders = Order.where(status: [:finished, :processing].map { |s| Order.statuses[s] }
Is there any better way?

As you want to call order statuses by it's names, the below should work
#orders = Order.where(status: Order.statuses[:finished, :processing])

Related

ActiveModel::MissingAttributeError: missing attribute: project_id when calling to_json

I wrote a below query class, which is throwing a missing attribute error which I didn't put in the select method anywhere. Any idea from where this error might be coming? Why project_id is being searched in the to_json method.
class WorkitemPriceResearchPresenter
def initialize(company)
#company = company
end
def query
#workitems ||= \
#company
.workitems
.joins(
:workitem_category,
:workitem_status,
{ project: [:shipyard, {vessel: :vessel_category}] },
)
.select <<-EOS
workitems.id as id,
projects.sequential_id as sequential_id,
vessels.name as vessel_name,
vessel_categories.code as vessel_category,
shipyards.name as shipyard,
workitems.item_code as item_code,
workitems.description as description,
workitems.unit as unit,
workitems.price_cents as price_cents,
workitems.currency as currency,
workitems.quantity as quantity,
workitem_statuses.name as status,
workitems.discount as discount,
DATE_PART('year', projects.scheduled_from::date) as scheduled_from_year,
projects.scheduled_from as scheduled_from,
DATE_PART('year', projects.scheduled_to::date) as scheduled_to_year,
projects.scheduled_to as scheduled_to,
workitem_categories.name as Category
EOS
end
def as_json
query.to_json
end
end
Error is:
pry(main)> WorkitemPriceResearchPresenter.new(company).query.as_json
Workitem Load (33.7ms) SELECT workitems.id as id,
projects.sequential_id as sequential_id,
vessels.name as vessel_name,
vessel_categories.code as vessel_category,
shipyards.name as shipyard,
workitems.item_code as item_code,
workitems.description as description,
workitems.unit as unit,
workitems.price_cents as price_cents,
workitems.currency as currency,
workitems.quantity as quantity,
workitem_statuses.name as status,
workitems.discount as discount,
DATE_PART('year', projects.scheduled_from::date) as scheduled_from_year,
projects.scheduled_from as scheduled_from,
DATE_PART('year', projects.scheduled_to::date) as scheduled_to_year,
projects.scheduled_to as scheduled_to,
workitem_categories.name as Category
FROM "workitems" INNER JOIN "workitem_categories" ON "workitem_categories"."id" = "workitems"."workitem_category_id" INNER JOIN "workitem_statuses" ON "workitem_statuses"."id" = "workitems"."workitem_status_id" INNER JOIN "projects" ON "projects"."id" = "workitems"."project_id" AND "projects"."deleted_at" IS NULL INNER JOIN "shipyards" ON "shipyards"."id" = "projects"."shipyard_id" AND "shipyards"."deleted_at" IS NULL INNER JOIN "vessels" ON "vessels"."id" = "projects"."vessel_id" AND "vessels"."deleted_at" IS NULL INNER JOIN "vessel_categories" ON "vessel_categories"."id" = "vessels"."vessel_category_id" AND "vessel_categories"."deleted_at" IS NULL WHERE "workitems"."deleted_at" IS NULL AND "workitems"."company_id" = $1 [["company_id", "c61e4368-1a60-464b-8002-9da31bf301e5"]]
ActiveModel::MissingAttributeError: missing attribute: project_id
from /Users/ar/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-4.2.11.3/lib/active_record/attribute_methods/read.rb:93:in `block in _read_attribute'
Let me know if I need to provide any more information.
Since you are joining workitems with project, your workitems is supposed to have a project_id attribute in order to join the tables. But it looks like you do not have such column, so you are getting this error.

Rails active record where chaining losing scope

Model Food has scope expired:
Food.rb
class Food < ApplicationRecord
default_scope { where.not(status: 'DELETED') }
scope :expired, -> { where('exp_date <= ?', DateTime.now) }
belongs_to :user
end
In my controller I'm chaining where conditions to filter foods by user and status:
query_type.rb
def my_listing_connection(filter)
user = context[:current_user]
scope = Food.where(user_id: user.id)
if filter[:status] == 'ARCHIVED'
# Line 149
scope = scope.where(
Food.expired.or(Food.where(status: 'COMPLETED'))
)
else
scope = scope.where(status: filter[:status])
end
scope.order(created_at: :desc, id: :desc)
# LINE 157
scope
end
Here is the rails log:
Food Load (2.7ms) SELECT `foods`.* FROM `foods` WHERE `foods`.`status` !=
'DELETED'
AND ((exp_date <= '2020-07-02 09:58:16.435609') OR `foods`.`status` = 'COMPLETED')
↳ app/graphql/types/query_type.rb:149
Food Load (1.6ms) SELECT `foods`.* FROM `foods` WHERE `foods`.`status` != 'DELETED'
AND `foods`.`user_id` = 1 ORDER BY `foods`.`created_at` DESC, `foods`.`id` DESC
↳ app/graphql/types/query_type.rb:157
Why does active records query loses expired scope (and a condition) in line 157?
It is ignored because where doesn't expect scopes like that. But you can use merge instead. Replace
scope = scope.where(
Food.expired.or(Food.where(status: 'COMPLETED'))
)
with
scope = scope.merge(Food.expired)
.or(Food.where(status: 'COMPLETED'))
or
scope = scope.where(status: 'COMPLETED').or(Food.expired)

How can I pass a hash for my where clause?

I am listing products and I want to be able to pass a hash as my where clause so I can do something like:
filter = {}
filter[:category_id] = #category.id
filter[:is_active] = true
#products = Products.where(filter)
Is it possible to do this somehow?
I also need to add something like this in my where clause:
WHERE price > 100
How could I add that to a filter?
The reason I want to do this is because in the UI I will have a set of optional filters, so then I will use if clauses in my controller to set each filter.
You can pass a hash to where exactly like you did:
filter = {
category_id: #category_id,
is_active: true
}
#products = Product.where(filter)
Using a hash only works for equality (e.g. category_id = 123), so you can't put something like price > 100 in there. To add that criteria, just add another where to the chain:
#product = Product.where(filter).where('price > 100')
Or...
#product = Product.where(filter)
if params[:min_price]
#product = #product.where('price > ?', min_price)
end
You could have a bit of fun with scopes: write a scope that's actually a mini predicate builder, sanitizing and pattern-matching strings, and delegating to the standard predicate builder for other scalar types. E.g.
# app/models/concerns/searchable.rb
module Searchable
extend ActiveSupport::Concern
included do
scope :search, ->(params) {
params.inject(self) do |rel, (key, value)|
next rel if value.blank?
case value
when String
rel.where arel_table[key].matches '%%%s%%' % sanitize_sql_like(value)
when Range, Numeric, TrueClass, FalseClass
rel.where key => value
else
raise ArgumentError, "unacceptable search type"
end
end
}
end
end
# app/models/product.rb
class Product < ApplicationRecord
include Searchable
then you can
filter = { name: 'cheese', description: 'aged', age: 42.. }
Product.search(filter) #=> SELECT "products".* FROM products WHERE "products"."name" ILIKE '%cheese%' AND "products"."description" ILIKE '%aged%' AND "products"."age" >= 42

How to add group_by to fix a postgresql group_by error

I am following a railscast episode but I'm using postgresql and am getting a group_by error:
PG::Error: ERROR: column "ratings.created_at" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT created_at, sum(score) as total_score FROM "ratings" ...
^
: SELECT created_at, sum(score) as total_score FROM "ratings" WHERE ("ratings"."created_at" BETWEEN '2014-01-02 00:00:00.000000' AND '2014-01-23 13:43:06.187741') GROUP BY date(created_at)
How should I modify my code below to include group_by created_at
def self.chart_data(start = 3.weeks.ago)
total_votes = votes_by_day(start)
(start.to_date..Date.today).map do |date|
{
created_at: date,
total_score: total_prices[date] || 0
}
end
end
def self.votes_by_day(start)
ratings = where(created_at: start.beginning_of_day..Time.zone.now)
ratings = ratings.group("date(created_at)")
ratings = ratings.select("created_at, sum(score) as total_score")
ratings.each_with_object({}) do |rating, scores|
scores[rating.created_at.to_date] = rating.total_score
end
end
Your group by clause and your select clause have different attributes. If you're grouping by "date(created_at)" then you can no longer select "created_at."
def self.votes_by_day(start)
ratings = where(created_at: start.beginning_of_day..Time.zone.now)
ratings = ratings.group("date(created_at)")
ratings = ratings.select("date(created_at), sum(score) as total_score")
ratings.each_with_object({}) do |rating, scores|
scores[rating.created_at.to_date] = rating.total_score
end

Render json and sort issue

This following is my model, For easier reading, I cut some code:
class Meeting < ActiveRecord::Base
def Joiners
# Never mind , this is a complex sql, i think you do not need to read it.
Joiners = Ploy.connection.select_all("SELECT count(users.id) as joiner FROM `ploys` INNER JOIN `participants` ON `participants`.`ploy_id` = `ploys`.`id` INNER JOIN `users` ON `users`.`id` = `participants`.`user_id` where `participants`.`ploy_id`= #{self.id}")
Joiners.rows[0][0]
end
def as_json(options={})
super(methods: :Joiners)
end
If i use
render json: #Meeting
It will render a JSON with Joiners attribute .
{
"Meeting":{
...
"Joiners":15,
...
}
"Meeting":{
...
"Joiners":13,
...
}
}
So:
How can i sort by Joiners ?
Thanks
You'll have to order it in a query result by using SQL ORDER BY

Resources