Skip to content

Commit

Permalink
Merge branch 'master' into aasm4
Browse files Browse the repository at this point in the history
  • Loading branch information
alto committed May 11, 2014
2 parents 7f9be4a + 2f31a6e commit 2d1ba39
Show file tree
Hide file tree
Showing 11 changed files with 274 additions and 21 deletions.
20 changes: 13 additions & 7 deletions .travis.yml
Expand Up @@ -5,7 +5,7 @@ rvm:
- 1.9.2
- 1.9.3
- 2.0.0
- 2.1.0
- 2.1
# - jruby-18mode # JRuby in 1.8 mode
- jruby-19mode # JRuby in 1.9 mode
- rbx-2.2.1
Expand All @@ -22,9 +22,15 @@ matrix:
- rvm: rbx-2.2.1
- rvm: jruby-19mode
exclude:
- { rvm: 1.8.7, gemfile: gemfiles/rails_4.0.gemfile }
- { rvm: 1.8.7, gemfile: gemfiles/rails_4.1.gemfile }
- { rvm: 1.9.2, gemfile: gemfiles/rails_4.0.gemfile }
- { rvm: 1.9.2, gemfile: gemfiles/rails_4.1.gemfile }
- { rvm: 1.9.3, gemfile: gemfiles/rails_4.1.gemfile }
- { rvm: jruby-19mode, gemfile: gemfiles/rails_4.1.gemfile }
- rvm: 1.8.7
gemfile: gemfiles/rails_4.0.gemfile
- rvm: 1.8.7
gemfile: gemfiles/rails_4.1.gemfile
- rvm: 1.9.2
gemfile: gemfiles/rails_4.0.gemfile
- rvm: 1.9.2
gemfile: gemfiles/rails_4.1.gemfile
- rvm: 1.9.3
gemfile: gemfiles/rails_4.1.gemfile
- rvm: jruby-19mode
gemfile: gemfiles/rails_4.1.gemfile
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -14,6 +14,10 @@

* deprecated old aasm_* class methods (old-style DSL), in preparation for AASM v4.0.0

## 3.2.0 (not yet released)

* support [Sequel](http://sequel.jeremyevans.net/) (see [issue #119](https://github.com/aasm/aasm/issues/119), thanks to [@godfat](https://github.com/godfat))

## 3.1.1

* bugfix: don't require ActiveRecord for localizing AASM event and state name (see [issue #113](https://github.com/aasm/aasm/issues/113), thanks to [@silentshade](https://github.com/silentshade))
Expand Down
3 changes: 2 additions & 1 deletion Gemfile
Expand Up @@ -5,7 +5,8 @@ gem "coveralls", :platforms => :ruby
gem 'rubysl', :platforms => :rbx
gem "jruby-openssl", :platforms => :jruby
gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
gem "rails", "3.2.15"
gem "rails", "3.2.18"
gem 'mongoid' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
gem 'sequel'

gemspec
33 changes: 25 additions & 8 deletions README.md
@@ -1,7 +1,7 @@
# AASM - Ruby state machines

<a href="http://badge.fury.io/rb/aasm"><img src="https://badge.fury.io/rb/aasm@2x.png" alt="Gem Version" height="18"></a>
[![Build Status](https://secure.travis-ci.org/aasm/aasm.png?branch=master)](http://travis-ci.org/aasm/aasm)
[![Build Status](https://travis-ci.org/aasm/aasm.svg?branch=master)](https://travis-ci.org/aasm/aasm)
[![Code Climate](https://codeclimate.com/github/aasm/aasm.png)](https://codeclimate.com/github/aasm/aasm)
[![Coverage Status](https://coveralls.io/repos/aasm/aasm/badge.png?branch=master)](https://coveralls.io/r/aasm/aasm)

Expand Down Expand Up @@ -278,6 +278,23 @@ class Job < ActiveRecord::Base
end
```

### Sequel

AASM also supports [Sequel](http://sequel.jeremyevans.net/) besides _ActiveRecord_ and _Mongoid_.

```ruby
class Job < Sequel::Model
include AASM

aasm do # default column: aasm_state
...
end
end
```

However it's not yet as feature complete as _ActiveRecord_. For example, there are
scopes defined yet. See [Automatic Scopes](#automatic-scopes).

### Mongoid

AASM also supports persistence to Mongodb if you're using Mongoid. Make sure
Expand Down Expand Up @@ -308,7 +325,7 @@ class Job < ActiveRecord::Base
state :cleaning
end

def sleeping
def self.sleeping
"This method name is in already use"
end
end
Expand All @@ -317,10 +334,10 @@ end
```ruby
class JobsController < ApplicationController
def index
@running_jobs = jobs.running
@recent_cleaning_jobs = jobs.cleaning.where('created_at >= ?', 3.days.ago)
@running_jobs = Job.running
@recent_cleaning_jobs = Job.cleaning.where('created_at >= ?', 3.days.ago)

# @sleeping_jobs = jobs.sleeping #=> "This method name is in already use"
# @sleeping_jobs = Job.sleeping #=> "This method name is in already use"
end
end
```
Expand Down Expand Up @@ -431,13 +448,13 @@ Given the `Job` class from above:
```ruby
job = Job.new

job.aasm.states
job.aasm.states.map(&:name)
=> [:sleeping, :running, :cleaning]

job.aasm.states(:permissible => true)
job.aasm.states(:permissible => true).map(&:name)
=> [:running]
job.run
job.aasm.states(:permissible => true)
job.aasm.states(:permissible => true).map(&:name)
=> [:cleaning, :sleeping]

job.aasm.events
Expand Down
3 changes: 2 additions & 1 deletion gemfiles/rails_3.2.gemfile
Expand Up @@ -6,7 +6,8 @@ gem 'rubysl', :platforms => :rbx
gem 'rubinius-developer_tools', :platforms => :rbx
gem "jruby-openssl", :platforms => :jruby
gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
gem "rails", "3.2.16"
gem "rails", "3.2.18"
gem 'mongoid' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
gem 'sequel'

gemspec :path => "../"
7 changes: 6 additions & 1 deletion gemfiles/rails_4.0.gemfile
Expand Up @@ -6,6 +6,11 @@ gem 'rubysl', :platforms => :rbx
gem 'rubinius-developer_tools', :platforms => :rbx
gem "jruby-openssl", :platforms => :jruby
gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
gem "rails", "4.0.2"
gem "rails", "4.0.5"

# mongoid is not yet compatible with Rails >= 4
# gem 'mongoid' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')

gem 'sequel'

gemspec :path => "../"
9 changes: 7 additions & 2 deletions gemfiles/rails_4.1.gemfile
Expand Up @@ -6,6 +6,11 @@ gem 'rubysl', :platforms => :rbx
gem 'rubinius-developer_tools', :platforms => :rbx
gem "jruby-openssl", :platforms => :jruby
gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
gem "rails", "4.1.0.beta1"
gem "rails", "4.1.1"

gemspec :path => "../"
# mongoid is not yet compatible with Rails >= 4
# gem 'mongoid' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')

gem 'sequel'

gemspec :path => "../"
3 changes: 3 additions & 0 deletions lib/aasm/persistence.rb
Expand Up @@ -12,6 +12,9 @@ def load_persistence(base)
elsif hierarchy.include?("Mongoid::Document")
require_files_for(:mongoid)
base.send(:include, AASM::Persistence::MongoidPersistence)
elsif hierarchy.include?("Sequel::Model")
require_files_for(:sequel)
base.send(:include, AASM::Persistence::SequelPersistence)
end
end

Expand Down
108 changes: 108 additions & 0 deletions lib/aasm/persistence/sequel_persistence.rb
@@ -0,0 +1,108 @@
module AASM
module Persistence
module SequelPersistence
def self.included(base)
base.send(:include, AASM::Persistence::Base)
base.send(:include, AASM::Persistence::SequelPersistence::InstanceMethods)
end

module InstanceMethods
def before_validation
aasm_ensure_initial_state
super
end

def before_create
aasm_ensure_initial_state
super
end

# Returns the value of the aasm_column - called from <tt>aasm.current_state</tt>
#
# If it's a new record, and the aasm state column is blank it returns the initial state
#
# class Foo < Sequel::Model
# include AASM
# aasm :column => :status do
# state :opened
# state :closed
# end
# end
#
# foo = Foo.new
# foo.current_state # => :opened
# foo.close
# foo.current_state # => :closed
#
# foo = Foo[1]
# foo.current_state # => :opened
# foo.aasm_state = nil
# foo.current_state # => nil
#
# NOTE: intended to be called from an event
#
# This allows for nil aasm states - be sure to add validation to your model
def aasm_read_state
state = send(self.class.aasm_column)
if new? && state.to_s.strip.empty?
aasm.determine_state_name(self.class.aasm.initial_state)
elsif state.nil?
nil
else
state.to_sym
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
#
# foo = Foo.new
# foo.aasm_state # => nil
# foo.valid?
# foo.aasm_state # => "open" (where :open is the initial state)
#
#
# foo = Foo.find(:first)
# foo.aasm_state # => 1
# foo.aasm_state = nil
# foo.valid?
# foo.aasm_state # => nil
#
def aasm_ensure_initial_state
aasm.enter_initial_state if
send(self.class.aasm_column).to_s.strip.empty?
end

# Writes <tt>state</tt> to the state column and persists it to the database
#
# foo = Foo[1]
# foo.aasm.current_state # => :opened
# foo.close!
# foo.aasm.current_state # => :closed
# Foo[1].aasm.current_state # => :closed
#
# NOTE: intended to be called from an event
def aasm_write_state state
aasm_column = self.class.aasm_column
update_ony({aasm_column => state.to_s}, aasm_column)
end

# Writes <tt>state</tt> to the state column, but does not persist it to the database
#
# foo = Foo[1]
# foo.aasm.current_state # => :opened
# foo.close
# foo.aasm.current_state # => :closed
# Foo[1].aasm.current_state # => :opened
# foo.save
# foo.aasm.current_state # => :closed
# Foo[1].aasm.current_state # => :closed
#
# NOTE: intended to be called from an event
def aasm_write_state_without_persistence state
send("#{self.class.aasm_column}=", state.to_s)
end
end
end
end
end
2 changes: 1 addition & 1 deletion spec/unit/persistence/mongoid_persistance_spec.rb
Expand Up @@ -147,6 +147,6 @@
end

rescue LoadError
puts "Not running Mongoid specs because mongoid gem if not installed!!!"
puts "Not running Mongoid specs because mongoid gem is not installed!!!"
end
end
103 changes: 103 additions & 0 deletions spec/unit/persistence/sequel_persistence_spec.rb
@@ -0,0 +1,103 @@

describe 'sequel' do
begin
require 'sequel'
require 'logger'
require 'spec_helper'

before(:all) do
db = Sequel.sqlite
# if you want to see the statements while running the spec enable the following line
# db.loggers << Logger.new($stderr)
db.create_table(:models) do
primary_key :id
String :status
end

@model = Class.new(Sequel::Model(db)) do
set_dataset(:models)
attr_accessor :default
include AASM
aasm :column => :status
aasm do
state :alpha, :initial => true
state :beta
state :gamma
event :release do
transitions :from => [:alpha, :beta, :gamma], :to => :beta
end
end
end
end

describe "instance methods" do
let(:model) {@model.new}

it "should respond to aasm persistence methods" do
expect(model).to respond_to(:aasm_read_state)
expect(model).to respond_to(:aasm_write_state)
expect(model).to respond_to(:aasm_write_state_without_persistence)
end

it "should return the initial state when new and the aasm field is nil" do
expect(model.aasm.current_state).to eq(:alpha)
end

it "should return the aasm column when new and the aasm field is not nil" do
model.status = "beta"
expect(model.aasm.current_state).to eq(:beta)
end

it "should return the aasm column when not new and the aasm_column is not nil" do
allow(model).to receive(:new?).and_return(false)
model.status = "gamma"
expect(model.aasm.current_state).to eq(:gamma)
end

it "should allow a nil state" do
allow(model).to receive(:new?).and_return(false)
model.status = nil
expect(model.aasm.current_state).to be_nil
end

it "should call aasm_ensure_initial_state on validation before create" do
expect(model).to receive(:aasm_ensure_initial_state).and_return(true)
model.valid?
end

it "should call aasm_ensure_initial_state before create, even if skipping validations" do
expect(model).to receive(:aasm_ensure_initial_state).and_return(true)
model.save(:validate => false)
end
end

describe 'subclasses' do
it "should have the same states as its parent class" do
expect(Class.new(@model).aasm.states).to eq(@model.aasm.states)
end

it "should have the same events as its parent class" do
expect(Class.new(@model).aasm.events).to eq(@model.aasm.events)
end

it "should have the same column as its parent even for the new dsl" do
expect(@model.aasm_column).to eq(:status)
expect(Class.new(@model).aasm_column).to eq(:status)
end
end

describe 'initial states' do
it 'should support conditions' do
@model.aasm do
initial_state lambda{ |m| m.default }
end

expect(@model.new(:default => :beta).aasm.current_state).to eq(:beta)
expect(@model.new(:default => :gamma).aasm.current_state).to eq(:gamma)
end
end

rescue LoadError
puts "Not running Sequel specs because sequel gem is not installed!!!"
end
end

0 comments on commit 2d1ba39

Please sign in to comment.