Reputation: 653
Say I have enum stage [:one, :two, :three, :four]
in a model.
When the controller action next_stage
is called (button clicked by the user to send it to the next stage), I want to go incrementally from stage X to Y. What's the easiest way to do this? I currently use a big, gross case statement, but I feel like I can do it better. I'll provide my use-case:
Class MyController
def next_stage
# @my_controller.stage => "two"
@my_controller.stage.value++ unless @my_controller.stage.four?
# @my_controller.stage => "three"
end
end
Upvotes: 2
Views: 1053
Reputation: 449
I don't know whether I get this right. Maybe you can try this:
Class MyController
def next_stage
@my_controller.stage.increment! :value rescue nil
end
end
Upvotes: 0
Reputation: 13949
There exists a gem simple_enum which lets you use more powerful enums.
The enum will let you use both integers and/or symbols, then you can do something like
@my_controller.stage.value_cd += 1 unless @my_controller.stage.last_stage?
You can use it with ActiveRecord/Mongoid, but also any other object.
For an ORM like Mongoid, your model could look like this
class Project
as_enum :status, {
idea: 0,
need_estimate: 1,
in_progress: 2,
finished: 3,
paid: 4,
field: { type: Integer, default: 0 }
def next_step!
self.status_cd += 1 unless self.paid?
end
end
project = Project.new
project.status # => :idea
project.need_estimate? #=> false
project.next_step!
project.need_estimate? #=> true
Upvotes: 0
Reputation: 1332
Honestly, if you're trying to store state that moves in a certain order, you should use a state machine. https://github.com/aasm/aasm supports using enums to store the state. You could do something like this;
aasm :column => :stage, :enum => true do
state :stage1, :initial => true
state :stage2
state :stage3
state :stage4
event :increment_stage do
transitions from: :stage1, to: :stage2
transitions from: :stage2, to: :stage3
transitions from: :stage3, to: :stage4
end
end
it not only cleans up your logic, but the tests will be simpler, and you can do all sorts of callbacks on different events. It's really good for any sort of workflow as well (say moving a post from review to approved etc.)
EDIT: Can I also suggest that if you don't want to use a state machine then you at least move this state shifting logic into your model? Controllers are about access control, models are about state.
Upvotes: 4
Reputation: 6411
NEW ANSWER
Ok, I think I better understand what you are looking for. How about this:
In your model set up your Enum with a hash rather than an array:
class MyClass < ActiveRecord::Base
enum stage: {one: 1, two: 2, three: 3, four: 4} #just makes more sense when talking about stages
end
Then you can use the current status' index:
Class MyController
def next_stage
# @my_controller.stage => "two"
current_stage = MyClass.stages[@my_controller.stage] # returns => 1
current_stage += 1 unless current_stage == 4 # returns => 2
@my_controller.update! stage: current_stage
# @my_controller.stage => "three"
end
end
If I understand the docs correctly, this may also work:
Class MyController
def next_stage
# @my_controller.stage => "two"
@my_controller.update! stage: MyClass.stages[@my_controller.stage] + 1 unless @my_controller.stage == :four
# @my_controller.stage => "three"
end
end
this is off the cuff and could probably be cleaned up in some ways. I can't experiment very much because I don't have something setup in a rails app with an enum to mess around with.
old answer left for archival purposes.
def next_stage
self.next
end
edit I saw enums and thought you were shortening enumerable to enum (as in x.to_enum
). Not so sure this won't work for you in some form. You asked for something other than an ugly case statement. You could use an enumerator that takes the current enum from that column to set the starting point, and the end point is 4 (or :four depending on how you write it) and have the rescue at the end of the enumerator return your max value.
Upvotes: -1
Reputation: 1082
Your code seems a bit strange, what is @my_controller
? What is stage
?
Besides that, if I understand correctly, what you want is this:
@my_controller[:stage] += 1 unless @my_controller.four?
Enumerations are stored as integers in the database, and they start at 0, with increments of 1. So, simply access the raw attribute data and increment it.
Upvotes: 0
Reputation: 1184
Sort of hackish but the only way to get an integer out of an enum that I have found is doing
model.read_attribute('foo')
So you could try to do
def next_stage
@my_controller.update_column(:stage, @my_controller.read_attribute('stage')+1) unless @my_controller.four?
end
Upvotes: 3