mirror of
https://github.com/aasm/aasm
synced 2023-03-27 23:22:41 -04:00
Merge pull request #124 from bkon/feature/enum-master
Support for edge rails enum fields.
This commit is contained in:
commit
6b7e84c4dd
4 changed files with 294 additions and 4 deletions
34
README.md
34
README.md
|
@ -278,6 +278,40 @@ class Job < ActiveRecord::Base
|
|||
end
|
||||
```
|
||||
|
||||
#### ActiveRecord enums
|
||||
|
||||
You can use
|
||||
[enumerations](http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html)
|
||||
in Rails 4.1+ for your state column:
|
||||
|
||||
```ruby
|
||||
class Job < ActiveRecord::Base
|
||||
include AASM
|
||||
|
||||
enum state {
|
||||
sleeping: 5,
|
||||
running: 99
|
||||
}
|
||||
|
||||
aasm :column => :state, :enum => true do
|
||||
state :sleeping, :initial => true
|
||||
state :running
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
You can explicitly pass the name of the method which provides access
|
||||
to the enumeration mapping as a value of ```enum```, or you can simply
|
||||
set it to ```true```. In the latter case AASM will try to use
|
||||
pluralized column name to access possible enum states.
|
||||
|
||||
Furthermore, if your column has integer type (which is normally the
|
||||
case when you're working with Rails enums), you can omit ```:enum```
|
||||
setting --- AASM auto-detects this situation and enabled enum
|
||||
support. If anything goes wrong, you can disable enum functionality
|
||||
and fall back to the default behavior by setting ```:enum```
|
||||
to ```false```.
|
||||
|
||||
### Sequel
|
||||
|
||||
AASM also supports [Sequel](http://sequel.jeremyevans.net/) besides _ActiveRecord_ and _Mongoid_.
|
||||
|
|
|
@ -18,6 +18,8 @@ module AASM
|
|||
|
||||
# use requires_new for nested transactions
|
||||
configure :requires_new_transaction, true
|
||||
|
||||
configure :enum, nil
|
||||
end
|
||||
|
||||
def initial_state(new_initial_state=nil)
|
||||
|
|
|
@ -85,10 +85,11 @@ module AASM
|
|||
# NOTE: intended to be called from an event
|
||||
def aasm_write_state(state)
|
||||
old_value = read_attribute(self.class.aasm_column)
|
||||
write_attribute(self.class.aasm_column, state.to_s)
|
||||
aasm_write_attribute state
|
||||
|
||||
success = if AASM::StateMachine[self.class].config.skip_validation_on_save
|
||||
self.class.where(self.class.primary_key => self.id).update_all(self.class.aasm_column => state.to_s) == 1
|
||||
success = if aasm_skipping_validations
|
||||
value = aasm_raw_attribute_value state
|
||||
self.class.where(self.class.primary_key => self.id).update_all(self.class.aasm_column => value) == 1
|
||||
else
|
||||
self.save
|
||||
end
|
||||
|
@ -113,10 +114,42 @@ module AASM
|
|||
#
|
||||
# NOTE: intended to be called from an event
|
||||
def aasm_write_state_without_persistence(state)
|
||||
write_attribute(self.class.aasm_column, state.to_s)
|
||||
aasm_write_attribute state
|
||||
end
|
||||
|
||||
private
|
||||
def aasm_enum
|
||||
case AASM::StateMachine[self.class].config.enum
|
||||
when false then nil
|
||||
when true then aasm_guess_enum_method
|
||||
when nil then aasm_guess_enum_method if aasm_column_looks_like_enum
|
||||
else AASM::StateMachine[self.class].config.enum
|
||||
end
|
||||
end
|
||||
|
||||
def aasm_column_looks_like_enum
|
||||
self.class.columns_hash[self.class.aasm_column.to_s].type == :integer
|
||||
end
|
||||
|
||||
def aasm_guess_enum_method
|
||||
self.class.aasm_column.to_s.pluralize.to_sym
|
||||
end
|
||||
|
||||
def aasm_skipping_validations
|
||||
AASM::StateMachine[self.class].config.skip_validation_on_save
|
||||
end
|
||||
|
||||
def aasm_write_attribute(state)
|
||||
write_attribute self.class.aasm_column, aasm_raw_attribute_value(state)
|
||||
end
|
||||
|
||||
def aasm_raw_attribute_value(state)
|
||||
if aasm_enum
|
||||
value = self.class.send(aasm_enum)[state]
|
||||
else
|
||||
value = state.to_s
|
||||
end
|
||||
end
|
||||
|
||||
# Ensures that if the aasm_state column is nil and the record is new
|
||||
# that the initial state gets populated before validation on create
|
||||
|
|
|
@ -23,6 +23,227 @@ describe "instance methods" do
|
|||
expect(gate).to respond_to(:aasm_write_state_without_persistence)
|
||||
end
|
||||
|
||||
describe "aasm_column_looks_like_enum" do
|
||||
subject { lambda{ gate.send(:aasm_column_looks_like_enum) } }
|
||||
|
||||
let(:column_name) { "value" }
|
||||
let(:columns_hash) { Hash[column_name, column] }
|
||||
|
||||
before :each do
|
||||
gate.class.stub(:aasm_column).and_return(column_name.to_sym)
|
||||
gate.class.stub(:columns_hash).and_return(columns_hash)
|
||||
end
|
||||
|
||||
context "when AASM column has integer type" do
|
||||
let(:column) { double(Object, type: :integer) }
|
||||
|
||||
it "returns true" do
|
||||
expect(subject.call).to be_true
|
||||
end
|
||||
end
|
||||
|
||||
context "when AASM column has string type" do
|
||||
let(:column) { double(Object, type: :string) }
|
||||
|
||||
it "returns false" do
|
||||
expect(subject.call).to be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "aasm_guess_enum_method" do
|
||||
subject { lambda{ gate.send(:aasm_guess_enum_method) } }
|
||||
|
||||
before :each do
|
||||
gate.class.stub(:aasm_column).and_return(:value)
|
||||
end
|
||||
|
||||
it "pluralizes AASM column name" do
|
||||
expect(subject.call).to eq :values
|
||||
end
|
||||
end
|
||||
|
||||
describe "aasm_enum" do
|
||||
subject { lambda{ gate.send(:aasm_enum) } }
|
||||
|
||||
context "when AASM enum setting contains an explicit enum method name" do
|
||||
let(:enum) { :test }
|
||||
|
||||
before :each do
|
||||
AASM::StateMachine[Gate].config.stub(:enum).and_return(enum)
|
||||
end
|
||||
|
||||
it "returns whatever value was set in AASM config" do
|
||||
expect(subject.call).to eq enum
|
||||
end
|
||||
end
|
||||
|
||||
context "when AASM enum setting is simply set to true" do
|
||||
before :each do
|
||||
AASM::StateMachine[Gate].config.stub(:enum).and_return(true)
|
||||
Gate.stub(:aasm_column).and_return(:value)
|
||||
gate.stub(:aasm_guess_enum_method).and_return(:values)
|
||||
end
|
||||
|
||||
it "infers enum method name from pluralized column name" do
|
||||
expect(subject.call).to eq :values
|
||||
expect(gate).to have_received :aasm_guess_enum_method
|
||||
end
|
||||
end
|
||||
|
||||
context "when AASM enum setting is explicitly disabled" do
|
||||
before :each do
|
||||
AASM::StateMachine[Gate].config.stub(:enum).and_return(false)
|
||||
end
|
||||
|
||||
it "returns nil" do
|
||||
expect(subject.call).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when AASM enum setting is not enabled" do
|
||||
before :each do
|
||||
AASM::StateMachine[Gate].config.stub(:enum).and_return(nil)
|
||||
Gate.stub(:aasm_column).and_return(:value)
|
||||
end
|
||||
|
||||
context "when AASM column looks like enum" do
|
||||
before :each do
|
||||
gate.stub(:aasm_column_looks_like_enum).and_return(true)
|
||||
gate.stub(:aasm_guess_enum_method).and_return(:values)
|
||||
end
|
||||
|
||||
it "infers enum method name from pluralized column name" do
|
||||
expect(subject.call).to eq :values
|
||||
expect(gate).to have_received :aasm_guess_enum_method
|
||||
end
|
||||
end
|
||||
|
||||
context "when AASM column doesn't look like enum'" do
|
||||
before :each do
|
||||
gate.stub(:aasm_column_looks_like_enum)
|
||||
.and_return(false)
|
||||
end
|
||||
|
||||
it "returns nil, as we're not using enum" do
|
||||
expect(subject.call).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when AASM is configured to use enum" do
|
||||
let(:state_sym) { :running }
|
||||
let(:state_code) { 2 }
|
||||
let(:enum_name) { :states }
|
||||
let(:enum) { Hash[state_sym, state_code] }
|
||||
|
||||
before :each do
|
||||
gate
|
||||
.stub(:aasm_enum)
|
||||
.and_return(enum_name)
|
||||
gate.stub(:aasm_write_attribute)
|
||||
gate.stub(:write_attribute)
|
||||
|
||||
gate
|
||||
.class
|
||||
.stub(enum_name)
|
||||
.and_return(enum)
|
||||
end
|
||||
|
||||
describe "aasm_write_state" do
|
||||
context "when AASM is configured to skip validations on save" do
|
||||
before :each do
|
||||
gate
|
||||
.stub(:aasm_skipping_validations)
|
||||
.and_return(true)
|
||||
end
|
||||
|
||||
it "passes state code instead of state symbol to update_all" do
|
||||
# stub_chain does not allow us to give expectations on call
|
||||
# parameters in the middle of the chain, so we need to use
|
||||
# intermediate object instead.
|
||||
obj = double(Object, update_all: 1)
|
||||
gate
|
||||
.class
|
||||
.stub(:where)
|
||||
.and_return(obj)
|
||||
|
||||
gate.aasm_write_state state_sym
|
||||
|
||||
expect(obj).to have_received(:update_all)
|
||||
.with(Hash[gate.class.aasm_column, state_code])
|
||||
end
|
||||
end
|
||||
|
||||
context "when AASM is not skipping validations" do
|
||||
it "delegates state update to the helper method" do
|
||||
# Let's pretend that validation is passed
|
||||
gate.stub(:save).and_return(true)
|
||||
|
||||
gate.aasm_write_state state_sym
|
||||
|
||||
expect(gate).to have_received(:aasm_write_attribute).with(state_sym)
|
||||
expect(gate).to_not have_received :write_attribute
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "aasm_write_state_without_persistence" do
|
||||
it "delegates state update to the helper method" do
|
||||
gate.aasm_write_state_without_persistence state_sym
|
||||
|
||||
expect(gate).to have_received(:aasm_write_attribute).with(state_sym)
|
||||
expect(gate).to_not have_received :write_attribute
|
||||
end
|
||||
end
|
||||
|
||||
describe "aasm_raw_attribute_value" do
|
||||
it "converts state symbol to state code" do
|
||||
expect(gate.send(:aasm_raw_attribute_value, state_sym))
|
||||
.to eq state_code
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when AASM is configured to use string field" do
|
||||
let(:state_sym) { :running }
|
||||
|
||||
before :each do
|
||||
gate
|
||||
.stub(:aasm_enum)
|
||||
.and_return(nil)
|
||||
end
|
||||
|
||||
describe "aasm_raw_attribute_value" do
|
||||
it "converts state symbol to string" do
|
||||
expect(gate.send(:aasm_raw_attribute_value, state_sym))
|
||||
.to eq state_sym.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "aasm_write_attribute helper method" do
|
||||
let(:sym) { :sym }
|
||||
let(:value) { 42 }
|
||||
|
||||
before :each do
|
||||
gate.stub(:write_attribute)
|
||||
gate.stub(:aasm_raw_attribute_value)
|
||||
.and_return(value)
|
||||
|
||||
gate.send(:aasm_write_attribute, sym)
|
||||
end
|
||||
|
||||
it "generates attribute value using a helper method" do
|
||||
expect(gate).to have_received(:aasm_raw_attribute_value).with(sym)
|
||||
end
|
||||
|
||||
it "writes attribute to the model" do
|
||||
expect(gate).to have_received(:write_attribute).with(:aasm_state, value)
|
||||
end
|
||||
end
|
||||
|
||||
it "should return the initial state when new and the aasm field is nil" do
|
||||
expect(gate.aasm.current_state).to eq(:opened)
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue