1
0
Fork 0
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:
Thorsten Böttger 2014-06-29 21:44:34 +02:00
commit 6b7e84c4dd
4 changed files with 294 additions and 4 deletions

View file

@ -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_.

View file

@ -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)

View file

@ -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

View file

@ -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