Query Rails Wildcard Enum Type - ruby-on-rails

Trying to figure out how to utilize a "wildcard" feature with rails enums, where I don't want to have a dedicated int/symbol mapping for a number of variations of types.
For example, I have a ticketing system, where the majority of tickets are unanswered or answered, but the user can also set a custom status in special cases.
class Ticket
enum status: { unanswered: 0, answered: 1, *wildcard: 2 }
end
I'd like to be able to query for Ticket.where(status: :wildcard) and return all tickets with a status value of 2. Digging into the rails source code, I think I want to override.
I figure I can skip the assert_valid_value validations by having a before_save callback that converts any non-existent strings to the right status code, but how would I query?

Related

How to get the id value from a Rails model enum?

Trying to understand how to use enums to help manage the different user status levels.
class User < ActiveRecord::Base
enum user_status: [:active, :pending, :trial]
end
So now active is 0, pending is 1, and trial is 2.
So if I do this:
user.active?
This works fine, but currently when I am setting the value I am still doing this:
user.user_status = 0
I was hoping I could do something like:
user.user_status = User.UserStatus.trial
The point is I don't want to have to remember what index each enum value is.
Also, if I change the order or add more user_status values, the index will change and I want to prevent bugs from me hardcoding the values in my codebase.
Is there a better way to handle the enum index values?
You can easily find the answer simply reading the documentation:
user.trial!
will set the status and update the record. For more variants you can refer to the docs.
it works in next way:
user.trial!
more detail

Rails combine searching by enum and find_or_initialize_by methods

this question is based on this one: Rails find_or_create_by with/without where
I've experienced some strange issue about enum & find_or_initialize_by methods in rails.
Let suppose I have the model Task.
class Task < AR::Base
enum status: { todo: 0, awaiting: 1, done: 2, another_one: 3, et.c. }
I want to find through todo and awaiting tasks, and if there is no exist, create a new one, but without a todo and awaiting value (status should be just nil). But, ActiveRecord::Enum strikes here!
This is the normal code in most cases:
Task.where(status: [Task.statuses[:todo], Task.statuses[:awaiting]]).find_or_initialize_by(title: 'epic')
But because of this string I get annoying '[0, 1]' is not a valid status exception. How can I avoid this exception without superfluous code?
For now I see just one relatively cool solution. Will be great if somebody another gives solution better.
Install gem 'active_record_union'
Use it!
Task.todo.union(Task.awaiting).union(Task.starting)
When querying an enum attribute you should use the symbol representing the enum and not the actual integer column value:
#task = Task.where(status: [:todo, :awaiting])
.find_or_initialize_by(title: 'epic')
Hence the '[0, 1]' is not a valid status since Rails is trying to be helpful and look up the enum values for you. In general you almost never need to use the actual integer value unless your are building a custom SQL string:
Task.where.not(status: :awaiting)
Rails also generates scopes based on the enum:
#tasks = Task.todo.awaiting.find_or_initialize_by(title: 'epic')
# or
#tasks = Task.todo.merge(Task.awaiting)
.find_or_initialize_by(title: 'epic')

Creating a status attribute for a model

I want to create a status attribute for my Taskmodel that would indicate where it is in a three part progress in this order: open => in-progress => complete. It would work in a way similar to how an Amazon package is delivered: ordered => shipped => delivered. I was wondering what would be the best way to setup this attribute. I may be wrong but creating three separate boolean attributes seemed sort've redundant. What's the best way to accomplish this?
Rails 4 has an built in enum macro. It uses a single integer column and maps to a list of keys.
class Order
enum status: [:ordered, :shipped, :delivered]
end
The maps the statuses as so: { ordered: 0, shipped: 1, delivered: 2}
It also creates scopes and "interrogation methods".
order.shipped?
Order.delivered.all
It will also map the enum values when writing queries with hash arguments:
Order.where(status: [:shipped, :delivered])
You should use the aasm gem. It has aasm_states for models, callback functionality etc.

Query/scope not including unsaved records in association

Scenario: I have a quiz type setup where Questions have many Answers and also have a Response provided by the user (omitted). A response has a selected attribute to indicate the user's choice and a correct? method that compares selected with the correct_answer.
Code: is here in this GitHub repo, along with seed data. Quick links to:
Question.rb
Answer.rb
Response.rb
Problem: I want to return all responses for a question that are correct however, unsaved records are not included.
I've tried a couple of different ways, as you'll see in the code including scope, question.correct_responses and also inverse_of (which I've read is supposed to be automatic now) - but to no avail.
Basically, I'm expecting following code to return 1, not 0.
q=Question.first
r=q.responses.build
r.selected = q.correct_answer
q.responses.correct.size # => 0??! wtf man!?
Your assistance is greatly appreciated.
When you use a scope you're going to the database.
Since you aren't saving the response, you don't want to go to the database. Instead, you should use something like the line below, which will select all of the question's "correct" responses and then count them.
q.responses.select { |r| r.correct? }.size
EDIT: or the short syntax for select:
q.responses.select(&:correct?).size

Notifications or user activity log implementation

The application has certain actions that are recorded for each user and recorded in db table notifications. The table has the following structure:
user_id, notification_type, credit, timestamp
notification_type does not store the entire text of the notification but just stores a short type description.
Later when the user wants to view his notifications I use a helper method from my view to fetch the actual text.
def notification_text(type)
case type_id
when 'flagPositive'
return 'A question you flagged has been marked as correct.'
when 'qAccepted'
return 'A question you added has been accepted.'
when 'qModerated'
return 'You moderated a question.'
when 'flagReport'
return 'You moderated a flag.'
end
end
1) Is this an optimum way to do this?
2) Should I replace the type_description with integer values (say 1 -> flagPositive, 2-> qAccepted) for performance benefits?
3) Are there any best practices around the same that I should be following?
1) This highly depends on your application and requirements. What I can say is that I used this approach sometimes and faced no problems so far.
2) If you see a performance problem with the string lookup, you could do so. A general recommendation is to optimize performance only when really needed.
3) Just google for "Ruby", "Rails", "ActiveRecord" and "Enum". You'll find lots of discussions about different solutions for this kind of problem. There are similar questions on this site, e.g., Enums in Ruby or In Rails, how should I implement a Status field for a Tasks app - integer or enum?

Resources