Get a enum with rails 7 - ruby-on-rails

On Ror 7.0, with postgres, I have a model created with the following migration:
create_enum :examen_cpf, ["attente formation", "attente qcm", "attente evaluations", "terminé"]
create_table :examen_cpfs do |t|
t.enum :state, enum_type: :examen_cpf, default: "attente formation", null: false
end
in a model I want to list the enum.
How can I get it ?
I try:
ExamenCpf.examen_cpf_enum
ExamenCpf.examen_cpf
....
Thanks for your help

You don't need a table, declass the enum in the class - the field in the table will just be an integer field

Related

Can you have an association based on a JSONB column in Rails?

I have a Rails app (rails v6.0.3, ruby 2.7.1) that is using the Noticed gem to send notifications. I have the following model configuration:
class Vendor < ApplicationRecord
has_noticed_notifications
end
The has_noticed_notifications is, as described in their README, a "Helper for associating and destroying Notification records where(params: {param_name.to_sym => self})"
So when I create a Notification like so...
VendorAddedNotification.with(
vendor: vendor,
data_source: "user",
).deliver(some_user) # => Notification inserted!
I expect to be able to find the Notifications that reference the vendor, using the Noticed method, like so:
vendor = Vendor.find ...
vendor.notifications_as_vendor # => Expected: [ Notification#123 ]
However, the input is always an empty array (Actual => [])
I looked at their source code and it looks like notifications_as_vendor is the following query:
Notification.where(params: { :vendor => self }) # where self = an instance of the Vendor model
However, that doesn't seem to work, and I'm not sure if it's supposed to or not. I tried running a simpler query to see if it worked ...
Notification.where(params: { :data_source => "user" })
But that did not work either. However, when I ran the same query with a different signature, it did:
Notification.where("params->>'data_source' = ?", "user")
So my question is-- is this Notified's mistake, or am I missing something in my configuration? I'm using PSQL for this, here is the relevant schema:
...
create_table "notifications", force: :cascade do |t|
t.string "recipient_type", null: false
t.bigint "recipient_id", null: false
t.string "type", null: false
t.jsonb "params"
t.datetime "read_at"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["read_at"], name: "index_notifications_on_read_at"
t.index ["recipient_type", "recipient_id"], name: "index_notifications_on_recipient_type_and_recipient_id"
end
...
And here are the related models:
class VendorAddedNotification < Noticed::Base
deliver_by :database
param :vendor
param :data_source
end
class Notification < ApplicationRecord
include Noticed::Model
belongs_to :recipient, polymorphic: true
end
Thank you in advance!
I've found why it's not working, it seems to be an issue with Notified.
In plain SQL I ran:
# PLAIN SQL
select "params" from "notifications" limit 1
Which returns the notification's params (returned notifcation's id=77)
# PLAIN SQL Result
"{""added_by"": {""_aj_globalid"": ""gid://stack-shine/WorkspaceMember/269""}, ""data_source"": ""user"", ""_aj_symbol_keys"": [""workspace_vendor"", ""data_source"", ""added_by""], ""workspace_vendor"": {""_aj_globalid"": ""gid://stack-shine/WorkspaceVendor/296""}}"
Now in Rails when I do
vendor = Notification.find(77).params[:vendor]
vendor.notifications_as_vendor.to_sql
The result is ...
"SELECT \"notifications\".* FROM \"notifications\" WHERE \"notifications\".\"params\" = '{\"vendor\":{\"_aj_globalid\":\"gid://stack-shine/Vendor/296\"},\"_aj_symbol_keys\":[\"vendor\"]}'"
... the extracted params from that query are:
'{\"vendor\":{\"_aj_globalid\":\"gid://stack-shine/Vendor/296\"},\"_aj_symbol_keys\":[\"vendor\"]}'
So ... In the database, the serialized params are A, but Rails is search for B:
# A: `params` In the database
"{""added_by"": {""_aj_globalid"": ""gid://stack-shine/WorkspaceMember/269""}, ""data_source"": ""user"", ""_aj_symbol_keys"": [""vendor"", ""data_source"", ""added_by""], ""vendor"": {""_aj_globalid"": ""gid://stack-shine/Vendor/296""}}"
# B: `params` Searched with by Rails
"{\"vendor\":{\"_aj_globalid\":\"gid://stack-shine/Vendor/296\"},\"_aj_symbol_keys\":[\"vendor\"]}"
Clearly this query could not work because the params in the database are not the params being search by Rails.
The notification, in the database, has extra parameters on top of "vendor" ("data_source" and "added_by") that are not being search up by the Vendor. Is this why it returns nothing?
For now, I'll simply the look up the notifications myself by storing the vendor_id in params and doing something like Notification.where("params >> vendor_id = ?", 123)

Number is out of range for ActiveModel::Type::Integer with limit 4

I created my migration with limit 8 in the chat_id column:
class CreateChat < ActiveRecord::Migration[5.0]
def change
create_table :bots do |t|
t.integer :user_chat_id, null: false, limit: 8, unique: true
...
t.timestamps
end
end
end
The migration is created perfectly and I can insert data into it.
But if I do:
class Chat < ApplicationRecord
self.primary_key = 'user_chat_id'
end
The following error occurs, 5187762395178250 is out of range for ActiveModel :: Type :: Integer with limit 4.
I researched and looked in the documentation, but I did not find anything about it.
Looks like you need to suppress the creation of the primary key, which by default is the id. So, rollback the migration and modify the migration file to include this line first:
create_table :bots, id: false do |t|
t.integer :user_chat_id, null: false, limit: 8, unique: true
...
t.timestamps
end ...
The options you applied to user_chat_id will make it the primary key.
We also deal with this error, here is our case:
Change DB type from int to bigint (MySQL table, column id)
When id column exceeded maximum int value (2.1 billion - unsigned integer), our app returned this error in some API
Restart puma server resolved it.
Just this simple command, it took us around 4 hours. :((
If you are using Unicorn or Passenger 5 or any others for your web server, try to restart it, I think it can resolve your issue.
Hope this helpful, thank you.

Is there a way to pass property.method to .where() instead of property in Rails?

My Schedule model looks like this:
create_table "schedules", force: :cascade do |t|
t.integer "week_day"
t.time "opening_time"
t.time "closing_time"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "taco_place_id"
end
add_index "schedules", ["taco_place_id"], name: "index_schedules_on_taco_place_id"
As you can see, there are opening_time and closing_time properties and I have a realtionship Schedule belongs_to :taco_place and TacoPlace has_many :schedules, dependent: :destroy.
What I am trying to do from the Schedule model is to get the actual schedule for a TacoPlace for today (if it exists).
I have already implemented a scope for having today's schedules for a TacoPlace (depending on the week_day property) that looks like this:
scope :today_for_taco_place, ->(taco_place){where(taco_place_id: taco_place.id, week_day: Time.now.wday)}
and I'm using it in this method:
def self.actual_for_taco_place(taco_place)
today = self.today_for_taco_place(taco_place)
today.where("opening_time <= :now and closing_time >= :now", now: Time.now.utc).first
end
I have tested it and it "works". The thing is that if I run "Schedule.first.opening_time" on the console I get "2000-01-01 06:00:00 UTC". As you can see, it does not only include the time, but also the day (even if it was seeded as "opening_time: "15:00".to_time, closing_time: "24:00".to_time").
Finally, here is the question:
Is there a way that I can run something like this: (I know this won't work yet)
def self.actual_for_taco_place(taco_place)
today = self.today_for_taco_place(taco_place)
today.where("#{opening_time.strftime("%H%M")} <= :now and #{closing_time.strftime("%H%M") >= :now", now: Time.now.utc.strftime("%H%M")).first
end
So that the .where() method doesn't look for the property (opening_time or closing_time), but rather perform the strftime() method so I can compare the time only? Or should I save the opening_time and closing_time as integers (i.e. "1200") or manually convert them in a method?
Sorry if my question was long or hard to understand. Thank you in advance for your advise.
Opening_time and closing_time are now integers. I figured out that I don't gain anything from it being a "time" instead of an "integer" since it is only representing an hour.

How to select objects based on their enum value and then sum another field belonging to the object collection in Rails

So, I'm using Rails 4, and I have an enum column on my "Sales_Opportunity" object called pipeline_status - this enables me to move it through a sales pipeline (e.g. New Lead, Qualified Lead, Closed deal etc). This all works fine. I'm able to find the number of sales_opportunities that a company has by status through using the following:
<%= #company.sales_opportunities.where(pipeline_status: 3).count %>
This all works fine. What I want to do is to find all sales_opportunities that have the pipeline_status of "closed_won" (enum value of 4 in my app) and sum the value of each won deal (so I can represent the total value of the customer based on the deals that are won in the system). A Sales_Opportunity in my model has a sale_value field, so I tried:
<%= #company.sales_opportunities.where(pipeline_status: 4).each.sale_value.sum %>
which returns the following error:
undefined method `sale_value' for #<Enumerator:0x007f9b87a9d128>
This is probably a trivial error but I can't for the life of me figure out what's going on. Is there where statement returning the enumerator or the sales_opportunity objects with that enumerator? Any help would be gratefully appreciated.
If it helps here are the fields in my sales_opportunities table:
create_table "sales_opportunities", force: true do |t|
t.datetime "close_date"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "pipeline_status", default: 0
t.string "opportunity_name"
t.integer "company_id"
t.decimal "sale_value", precision: 15, scale: 2, default: 0.0
end
A Sales_opportunity belongs_to a Company Object and a User Object, if that makes any difference.
use aggregate function sum
<%= #company.sales_opportunities.where(pipeline_status: 4).sum(:sale_value) %>
Other possibility is to use
<%= #company.sales_opportunities.where(pipeline_status: 4).pluck(:sale_value).reduce(0, :+) %>

shema_plus with rails migration: make combined columns unique

I'm using the shema_plus gem in rails, and I want to make it so I am unable to duplicate data into my database if the combination of two columns is the same.
shouldn't this work?:
t.string :name, null: false, index: { with: :deleted_at, unique: true }
':deleted_at' is definitely a field in my migration.
but this allows me to enter in the same name twice on the mysql side.
also, here is some info that mysql gives me:
Index: index_plans_on_name_and_deleted_at
Definition:
Type BTREE
Unique Yes
Columns name
deleted_at
EDIT_____________________________________________________
It looks like I needed a default value for deleted_at, like :
t.datetime :deleted_at, default: '2000-01-01'
Not sure about the gem, but you can directly achieve this in ActiveRecord using:
validates :name, :uniqueness => {:scope => :deleted_at}
assuming you want the combination of name and deleted_at to be unique.

Resources