How to get the id value from a Rails model enum? - ruby-on-rails

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

Related

Is there a more idiomatic way to update an ActiveRecord attribute hash value?

Given a person ActiveRecord instance: person.phones #=> {home: '00123', office: '+1-45'}
Is there a more Ruby/Rails idiomatic way to do the following:
person_phones = person.phones
person_phones[:home] = person_phones[:home].sub('001', '+1')
person.update_column :phones, person_phones
The example data is irrelevant.
I only want to sub one specific hash key value and the new hash to be saved in the database. I was wondering if there was a way to do this just calling person.phones once, and not multiple times
Without changing much behaviour:
person.phones[:home].sub!('001', '+1')
person.save
There are a few important differences here:
You modify the string object by using sub! instead of sub. Meaning that all other variables/objects that hold a reference to the string will also change.
I'm using save instead of update_column. This means callbacks will not be skipped and all changes are saved instead of only the phones attribute.
From the comment I make out you're looking for a one liner, which isn't mutch different from the above:
person.tap { |person| person.phones[:home].sub!('001', '+1') }.save
You can use the before_validation callback on your model.
Like this:
class Phone < ApplicationRecord
validates :home, US_PHONE_REGEX
before_validation :normalize_number
private
def normalize_number
home.gsub!(/^001/, '+1')
end
end
Note: I haven't tested this code, it's meant to show an approach only.
If you're looking to normalize also an international number, evaluate if the use of a lib like phony wouldn't make more sense, or the rails lib https://github.com/joost/phony_rails based on it.
EDIT
since the comment clarify you only want to change the values of the hash in one like you can use Ruby's method transform_values!:
phones.transform_values!{|v| v.gsub(/^001/, '+1')}

How to set parent attribute based on child attributes

I have a situation where a parent Order has multiple child Items. Both Order and Item have a status_id column. I want the user to update the status_id of the Item, and then when all of the Items have a status_id, then the Order's status_id should be auto-set (to some value based on what Item status_ids are).
The code that I currently have is this:
class Item
after_save :set_order_status_id
def set_order_status_id
if self.order.items.where(status_id:nil).blank?
self.order.update_attributes(status_id:X)
end
end
end
Ths is pretty smelly code because it violates SRP, uses a callback, and is pretty inefficient, considering that this means if an Order has 5 Items and all 5 Items are being updated, after EACH Item update, the set_order_status_id method is called, and a database query is run.
So... is there a better way of writing this to avoid these issues? Particularly I'm interested in removing the inefficiency with constantly checking the parent Order's other child Items' statuses... because again if an all 5 Items is updated at once, it's silly to check after each and every update when it should just wait until the 5th update.... Does Rails have a magical way of doing this?
The answer is no, there is no magic way to do that using the framework. Your best option is to use a customised solution and run your check only after your items update. Something like:
...your code...
order.items.update_all(whatever) <-- update items
update_status(order) <-- update order status
...
def update_status(order)
return if order.items.where(status_id: nil).exist? <-- more efficient
update_attributes(status_id: X)
end
the method could also be in the Offer model for simplicity.
If the order status can be derived from the item status at any point in time, it may be better to avoid setting it in the database entirely. You can instead create an accessor to query it on-demand:
class Order < ActiveRecord::Base
def status
# memoize the calculation, including nils
return #status if defined? #status
item_statuses = items.pluck(:status).uniq
# your logic here:
# 1. check for any nils
# 2. check for any 'pending', etc.
#status = 'pending'
end
end
Whether this alternate solution fits your needs depends on your database read patterns.

decrement a value in view but not database

I have a total value that I would like to keep but I would also like to decrement it every time a certain kind of user creates the resource. For example I have-
if #user == "Premium"
#resource.decrement!(:total)
end
But I would also like to show the first :total that I had. So in other words I would like the original value and the decremented value to show on the view page. like so-
<%= resource.total %>
<%= resource.decremented_value %>
how could I do that? Would I need to create another column in my database?
I'd recommend creating a separate column and then using a new method in your model to decrement the value. For example:
in models/resource.rb
before_create :decrement
def decrement
self.decremented_value = self.total - 1
end
This will only call the decrement method when you create a Resource for the first time. If you edit an existing Resource's total however, it won't decrement the total as it's only listening for create actions. I would suggest looking at this answer for more on listening to updates in the model: https://stackoverflow.com/a/1586641/810794
One last thing: the naming of your deceremented_value and total columns is a little confusing. You may want to change them to current_total and previous_total as these seem to better describe what you're trying to achieve.
Thank you #lukad03,
I did some messing around with your answer and figured out another way myself. I created another column to mimic the first and just made it equal to itself in the resource model using a variable. (like #lukad03 said)
resource.rb
before_create :decrement
def decrement
self.total = self.decrement
end
It is a little bit of a hack but it works just fine.

Neo4j gem - Query dependant on parameter values

This might not be as much neo4j as it is rails. But maybe I'm wrong
I'm looking for suggestions on handling a situation when parameters are specific values. For example. In my query
#friends_events = current_user.friends.events(:e, :rel).where("rel.admin = {admin_p} AND e.size_max < {size_p}", uuid: #event_id, primary_category: params[:primary_category] ).params(admin_p: true, size_p: params[:group_max].to_i)
The event has a size_max property which can be an integer or it can be any. Right now I have any as a blank value. Basically if they choose any, I need to ignore that parameter all together in the query (or handle it in a similar fashion). A way to cheat it is to handle the situation outside and if any is selected and the value is blank, I manually set it to a really high number which won't be hit.
Not sure how to do this either inside or outside the query without an odd hacky way right now. Suggestions?
Update
I have my query as you suggested. and the methods to deal with the 'validation'
#friends_events = current_user.friends.events(:e, :rel).where("rel.admin = {admin_p} #{size_string}", uuid: #event_id, primary_category: params[:primary_category] ).params(admin_p: true, size_p: size_param)
And I changed the size_param to blank which it doesn't like. I wanted to be able to handle both cases. e.g. when you first hit the page params is empty, and when you submit, it's blank. Nil will work with scenario 1 and not scenario 2. Do I need a || case here?
def size_string
'AND e.size_max < {size_p}' if params[:group_max]
end
def size_param
#has not taken in blank scenario
params[:group_max].blank? ? false : params[:group_max].to_i
end
and in my view I have
<% if !#friends_events.blank? %>
My error is
Don't know how to compare that. Left: 0 (Long); Right: false (Boolean) from the line above in my view. Changing .blank? to .nil? allows the filter to go through (though incorrectly)
The ways of handling it that you identified seem like the best options. It may be a little more work to evaluate it before hitting the database but you'll get a performance boost by omitting that property entirely if the user wants any. I have been told that filtering using > or <, does not use indexes, so it may be slow if you have enough records. Start with something like this:
def where_this_happens
#friends_events = current_user.friends.events(:e, :rel)
.where("rel.admin = {admin_p} #{size_string}", uuid: #event_id, primary_category: params[:primary_category] )
.params(admin_p: true, size_p: size_param)
end
def size_string
'AND e.size_max < {size_p}' unless params[:group_max].blank?
end
def size_param
params[:group_max].nil? ? false : params[:group_max].to_i
end
There's no harm in setting the size_p param and not using it but I'm pretty sure it will bark at you if you feed it nil.

Rails: How can I set multiple attributes to the same value upon create?

Let's say I'm keeping track of a character's health in my HP model.
I have HP.min, HP.max and HP.current, three attributes which track the characters minimum HP, maximum HP and current HP respectively.
I have a form that allows players to input their minimum and maximum HP. I'd like to then set HP.current to the value of params[:max] upon creation, as having the player entire the same value for their current and maximum HP seems super redundant.
What's the best way to accomplish this? I've tried a few ActiveRecord callbacks (after_save, before_create), but have had no luck.
Either do it in the model:
before_save { |hp| hp.current = hp.max }
Or do something in the controller:
def update
#hp.current = params[:max] if params[:max]
if #hp.update_attributes(params[:hp])
# your options here
else
# your options here
end
How are you currently using the callbacks? Perhaps you could post some code so we could see if there is a problem with the implementation. :)
Alternatively, I assume that the form values would be passed to a controller and you could build the record manually by setting max => params[:max], current => params[:max] before saving the record.

Resources