Skip to content

Commit

Permalink
Merge branch 'feature/enum' into feature/enum-master
Browse files Browse the repository at this point in the history
Conflicts:
        README.md
  • Loading branch information
bkon committed May 10, 2014
2 parents 2f31a6e + ab6d184 commit 82aecad
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 4 deletions.
25 changes: 25 additions & 0 deletions README.md
Expand Up @@ -278,6 +278,31 @@ 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 => :states do
state :sleeping, :initial => true
state :running
end
end
```

You need to pass the name of the method which provides access to the
enumeration mapping as a value of ```enum```.

### Sequel

AASM also supports [Sequel](http://sequel.jeremyevans.net/) besides _ActiveRecord_ and _Mongoid_.
Expand Down
2 changes: 2 additions & 0 deletions lib/aasm/base.rb
Expand Up @@ -18,6 +18,8 @@ def initialize(clazz, options={}, &block)

# use requires_new for nested transactions
configure :requires_new_transaction, true

configure :enum, nil
end

def initial_state(new_initial_state=nil)
Expand Down
28 changes: 24 additions & 4 deletions lib/aasm/persistence/active_record_persistence.rb
Expand Up @@ -85,10 +85,11 @@ module InstanceMethods
# 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
Expand All @@ -113,10 +114,29 @@ def aasm_write_state(state)
#
# 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
AASM::StateMachine[self.class].config.enum
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
Expand Down
112 changes: 112 additions & 0 deletions spec/unit/persistence/active_record_persistence_spec.rb
Expand Up @@ -23,6 +23,118 @@
expect(gate).to respond_to(:aasm_write_state_without_persistence)
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
Expand Down

0 comments on commit 82aecad

Please sign in to comment.