Add support for Nobrainer (RethinkDB)

* Add persistence for NoBrainer
* Add tests for NoBrainer
* Show the ORM version when running the tests
* Add a Dockerfile and a docker-compose.yml file allowing to run from anywhere very easily the test suite with `docker-compose run --rm aasm\'
* Moves the Mongoid database name definition in the gem loader (avoiding code repetition)
* Update the CHANGELOG.md and the README.md files
* Add Rails generator for NoBrainer
This commit is contained in:
Guillaume Hain 2018-01-12 11:41:12 +01:00 committed by Anil Kumar Maurya
parent 7a59852f53
commit 65073e360a
31 changed files with 1037 additions and 40 deletions

View File

@ -11,12 +11,17 @@ services:
- mongodb
- redis-server
addons:
rethinkdb: '2.3.6'
gemfile:
- gemfiles/rails_3.2.gemfile
- gemfiles/rails_4.0.gemfile
- gemfiles/rails_4.2.gemfile
- gemfiles/rails_4.2_mongoid_5.gemfile
- gemfiles/rails_4.2_nobrainer.gemfile
- gemfiles/rails_5.0.gemfile
- gemfiles/rails_5.0_nobrainer.gemfile
before_script:
- mkdir /tmp/dynamodb

View File

@ -1,47 +1,58 @@
appraise 'rails_3.2' do
gem 'rails', '~>3.2.22'
gem 'mongoid', '~>3.1'
gem 'rails', '~> 3.2.22'
gem 'mongoid', '~> 3.1'
gem 'sequel'
gem 'bson_ext', :platforms => :ruby
gem 'bson_ext', platforms: :ruby
gem 'test-unit', '~> 3.0'
gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', :platforms => :jruby
gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', platforms: :jruby
end
appraise 'rails_4.0' do
gem 'mime-types', '~> 2', :platforms => [:ruby_19, :jruby]
gem 'mime-types', '~> 2', platforms: %i[ruby_19 jruby]
gem 'rails', '4.0.13'
gem 'mongoid', '~>4.0'
gem 'mongoid', '~> 4.0'
gem 'sequel'
gem 'dynamoid', '~> 1', :platforms => :ruby
gem 'aws-sdk', '~>2', :platforms => :ruby
gem 'dynamoid', '~> 1', platforms: :ruby
gem 'aws-sdk', '~> 2', platforms: :ruby
gem 'redis-objects'
gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', :platforms => :jruby
gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', platforms: :jruby
end
appraise 'rails_4.2' do
gem 'nokogiri', '1.6.8.1', :platforms => [:ruby_19]
gem 'mime-types', '~> 2', :platforms => [:ruby_19, :jruby]
gem 'nokogiri', '1.6.8.1', platforms: %i[ruby_19]
gem 'mime-types', '~> 2', platforms: %i[ruby_19 jruby]
gem 'rails', '4.2.5'
gem 'mongoid', '~>4.0'
gem 'mongoid', '~> 4.0'
gem 'sequel'
gem 'dynamoid', '~> 1', :platforms => :ruby
gem 'aws-sdk', '~>2', :platforms => :ruby
gem 'dynamoid', '~> 1', platforms: :ruby
gem 'aws-sdk', '~> 2', platforms: :ruby
gem 'redis-objects'
gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', :platforms => :jruby
gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', platforms: :jruby
end
appraise 'rails_4.2_nobrainer' do
gem 'rails', '4.2.5'
gem 'nobrainer', '~> 0.33.0'
gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', platforms: :jruby
end
appraise 'rails_4.2_mongoid_5' do
gem 'mime-types', '~> 2', :platforms => [:ruby_19, :jruby]
gem 'mime-types', '~> 2', platforms: %i[ruby_19 jruby]
gem 'rails', '4.2.5'
gem 'mongoid', '~>5.0'
gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', :platforms => :jruby
gem 'mongoid', '~> 5.0'
gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', platforms: :jruby
end
appraise 'rails_5.0' do
gem 'rails', '5.0.0'
gem 'mongoid', '~>6.0'
gem 'mongoid', '~> 6.0'
gem 'sequel'
gem 'dynamoid', '~> 1', :platforms => :ruby
gem 'aws-sdk', '~>2', :platforms => :ruby
gem 'dynamoid', '~> 1', platforms: :ruby
gem 'aws-sdk', '~> 2', platforms: :ruby
gem 'redis-objects'
end
appraise 'rails_5.0_nobrainer' do
gem 'rails', '5.0.0'
gem 'nobrainer', '~> 0.33.0'
end

View File

@ -1,6 +1,9 @@
# CHANGELOG
## unreleased
* Add support for Nobrainer (RethinkDB)
## 4.12.3
* Add to AASM fire(event) and fire!(event) methods [#494](https://github.com/aasm/aasm/pull/494), thanks to [slayer](https://github.com/slayer)

44
Dockerfile Normal file
View File

@ -0,0 +1,44 @@
FROM ruby:2.3.4-slim
LABEL maintainer="AASM"
ENV DEBIAN_FRONTEND noninteractive
# ~~~~ System locales ~~~~
RUN apt-get update && apt-get install -y locales && \
dpkg-reconfigure locales && \
locale-gen C.UTF-8 && \
/usr/sbin/update-locale LANG=C.UTF-8 && \
echo 'en_US.UTF-8 UTF-8' >> /etc/locale.gen && \
locale-gen
# Set default locale for the environment
ENV LC_ALL C.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US.UTF-8
ENV APP_HOME /application
# ~~~~ Application dependencies ~~~~
RUN apt-get update
RUN apt-get install -y libsqlite3-dev \
build-essential \
git
# ~~~~ Bundler ~~~~
RUN gem install bundler
WORKDIR $APP_HOME
RUN mkdir -p $APP_HOME/lib/aasm/
COPY Gemfile* $APP_HOME/
COPY *.gemspec $APP_HOME/
COPY lib/aasm/version.rb $APP_HOME/lib/aasm/
ENV BUNDLE_GEMFILE=$APP_HOME/Gemfile \
BUNDLE_JOBS=8 \
BUNDLE_PATH=/bundle
RUN bundle install
# ~~~~ Import application ~~~~
COPY . $APP_HOME

View File

@ -24,6 +24,7 @@
- [Sequel](#sequel)
- [Dynamoid](#dynamoid)
- [Mongoid](#mongoid)
- [Nobrainer](#nobrainer)
- [Redis](#redis)
- [Automatic Scopes](#automatic-scopes)
- [Transaction support](#transaction-support)
@ -52,10 +53,8 @@
This package contains AASM, a library for adding finite state machines to Ruby classes.
AASM started as the *acts_as_state_machine* plugin but has evolved into a more generic library
that no longer targets only ActiveRecord models. It currently provides adapters for
[ActiveRecord](http://api.rubyonrails.org/classes/ActiveRecord/Base.html),
and [Mongoid](http://mongoid.org/) but it can be used for any Ruby class, no matter what
parent class it has (if any).
that no longer targets only ActiveRecord models. It currently provides adapters for many
ORMs but it can be used for any Ruby class, no matter what parent class it has (if any).
## Upgrade from version 3 to 4
@ -825,6 +824,23 @@ class Job
end
```
### NoBrainer
AASM also supports persistence to [RethinkDB](https://www.rethinkdb.com/)
if you're using [Nobrainer](http://nobrainer.io/).
Make sure to include NoBrainer::Document before you include AASM.
```ruby
class Job
include NoBrainer::Document
include AASM
field :aasm_state
aasm do
...
end
end
```
### Redis
AASM also supports persistence in Redis via

40
docker-compose.yml Normal file
View File

@ -0,0 +1,40 @@
version: "2"
services:
aasm:
image: aasm/aasm
build: .
command: bash -c 'bundle exec appraisal install && bundle exec appraisal rspec'
environment:
- DYNAMODB_HOST=dynamodb
- DYNAMODB_PORT=8000
- MONGODB_HOST=mongo
- MONGODB_PORT=27017
- RAILS_ENV=development
- REDIS_HOST=redis
- REDIS_PORT=6379
- RETHINKDB_DB=rethinkdb_test
- RETHINKDB_HOST=rethinkdb
- RETHINKDB_PORT=28015
depends_on:
- dynamodb
- mongo
- redis
- rethinkdb
volumes:
- .:/application
volumes_from:
- bundle
bundle:
image: aasm/aasm
command: echo Bundler data container
volumes:
- /bundle
dynamodb:
image: cnadiminti/dynamodb-local:2017-02-16
mongo:
image: mongo:3.6.1
redis:
image: redis:4.0.6-alpine
rethinkdb:
image: rethinkdb:2.3.6

View File

@ -0,0 +1,9 @@
# This file was generated by Appraisal
source "https://rubygems.org"
gem "sqlite3", platforms: :ruby
gem "rails", "5.1.3"
gem "nobrainer", "~>0.33.0"
gemspec path: "../"

View File

@ -0,0 +1,9 @@
# This file was generated by Appraisal
source "https://rubygems.org"
gem "sqlite3", platforms: :ruby
gem "rails", "5.1.3"
gem "nobrainer", "~>0.33.0"
gemspec path: "../"

View File

@ -12,6 +12,9 @@ module AASM
elsif hierarchy.include?("Mongoid::Document")
require_persistence :mongoid
include_persistence base, :mongoid
elsif hierarchy.include?("NoBrainer::Document")
require_persistence :no_brainer
include_persistence base, :no_brainer
elsif hierarchy.include?("Sequel::Model")
require_persistence :sequel
include_persistence base, :sequel

View File

@ -0,0 +1,105 @@
require 'aasm/persistence/orm'
module AASM
module Persistence
module NoBrainerPersistence
# This method:
#
# * extends the model with ClassMethods
# * includes InstanceMethods
#
# Adds
#
# before_validation :aasm_ensure_initial_state
#
# As a result, it doesn't matter when you define your methods - the following 2 are equivalent
#
# class Foo
# include NoBrainer::Document
# def aasm_write_state(state)
# "bar"
# end
# include AASM
# end
#
# class Foo
# include NoBrainer::Document
# include AASM
# def aasm_write_state(state)
# "bar"
# end
# end
#
def self.included(base)
base.send(:include, AASM::Persistence::Base)
base.send(:include, AASM::Persistence::ORM)
base.send(:include, AASM::Persistence::NoBrainerPersistence::InstanceMethods)
base.extend AASM::Persistence::NoBrainerPersistence::ClassMethods
base.after_initialize :aasm_ensure_initial_state
end
module ClassMethods
def aasm_create_scope(state_machine_name, scope_name)
scope_options = lambda {
where(aasm(state_machine_name).attribute_name.to_sym => scope_name.to_s)
}
send(:scope, scope_name, scope_options)
end
end
module InstanceMethods
private
def aasm_save
self.save
end
def aasm_raise_invalid_record
raise NoBrainer::Error::DocumentInvalid.new(self)
end
def aasm_supports_transactions?
false
end
def aasm_update_column(attribute_name, value)
write_attribute(attribute_name, value)
save(validate: false)
true
end
def aasm_read_attribute(name)
read_attribute(name)
end
def aasm_write_attribute(name, value)
write_attribute(name, value)
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::StateMachineStore.fetch(self.class, true).machine_names.each do |name|
aasm_column = self.class.aasm(name).attribute_name
aasm(name).enter_initial_state if read_attribute(aasm_column).blank?
end
end
end # InstanceMethods
end
end # Persistence
end # AASM

View File

@ -0,0 +1,28 @@
require 'rails/generators/named_base'
require 'generators/aasm/orm_helpers'
module NoBrainer
module Generators
class AASMGenerator < Rails::Generators::NamedBase
include AASM::Generators::OrmHelpers
namespace 'nobrainer:aasm'
argument :column_name, type: :string, default: 'aasm_state'
def generate_model
invoke 'nobrainer:model', [name] unless model_exists?
end
def inject_aasm_content
inject_into_file model_path, model_contents, after: "include NoBrainer::Document::Timestamps\n" if model_exists?
end
def inject_field_types
inject_into_file model_path, migration_data, after: "include NoBrainer::Document::Timestamps\n" if model_exists?
end
def migration_data
" field :#{column_name}"
end
end
end
end

View File

@ -16,6 +16,7 @@ exclude_files = [
'aasm/persistence/active_record_persistence.rb',
'aasm/persistence/dynamoid_persistence.rb',
'aasm/persistence/mongoid_persistence.rb',
'aasm/persistence/no_brainer_persistence.rb',
'aasm/persistence/sequel_persistence.rb',
'aasm/persistence/redis_persistence.rb'
]

View File

@ -0,0 +1,29 @@
require 'spec_helper'
if defined?(NoBrainer::Document)
require 'generator_spec'
require 'generators/nobrainer/aasm_generator'
describe NoBrainer::Generators::AASMGenerator, type: :generator do
destination File.expand_path('../../../tmp', __FILE__)
before(:all) do
prepare_destination
end
it 'creates model with aasm block for default column_name' do
run_generator %w[user]
assert_file 'app/models/user.rb', /include AASM\n\n aasm do\n end\n/
end
it 'creates model with aasm block for custom column_name' do
run_generator %w[user state]
assert_file 'app/models/user.rb', /aasm :column => 'state' do\n end\n/
end
it 'creates model with aasm block for namespaced model' do
run_generator %w[Admin::User state]
assert_file 'app/models/admin/user.rb', /aasm :column => 'state' do\n end\n/
end
end
end

View File

@ -0,0 +1,36 @@
class ComplexNoBrainerExample
include NoBrainer::Document
include AASM
field :left, type: String
field :right, type: String
aasm :left, column: 'left' do
state :one, initial: true
state :two
state :three
event :increment do
transitions from: :one, to: :two
transitions from: :two, to: :three
end
event :reset do
transitions from: :three, to: :one
end
end
aasm :right, column: 'right' do
state :alpha, initial: true
state :beta
state :gamma
event :level_up do
transitions from: :alpha, to: :beta
transitions from: :beta, to: :gamma
end
event :level_down do
transitions from: :gamma, to: :beta
transitions from: :beta, to: :alpha
end
end
end

View File

@ -0,0 +1,39 @@
class InvalidPersistorNoBrainer
include NoBrainer::Document
include AASM
field :name
field :status
aasm :left, column: :status, skip_validation_on_save: true do
state :sleeping, initial: true
state :running
event :run do
transitions to: :running, from: :sleeping
end
event :sleep do
transitions to: :sleeping, from: :running
end
end
validates_presence_of :name
end
class MultipleInvalidPersistorNoBrainer
include NoBrainer::Document
include AASM
field :name
field :status
aasm :left, column: :status, skip_validation_on_save: true do
state :sleeping, initial: true
state :running
event :run do
transitions to: :running, from: :sleeping
end
event :sleep do
transitions to: :sleeping, from: :running
end
end
validates_presence_of :name
end

View File

@ -0,0 +1,21 @@
class NoScopeNoBrainer
include NoBrainer::Document
include AASM
field :status, type: String
aasm create_scopes: false, column: :status do
state :ignored_scope
end
end
class NoScopeNoBrainerMultiple
include NoBrainer::Document
include AASM
field :status, type: String
aasm :left, create_scopes: false, column: :status do
state :ignored_scope
end
end

View File

@ -0,0 +1,25 @@
class Parent
include NoBrainer::Document
include AASM
field :status, type: String
has_many :childs
aasm column: :status do
state :unknown_scope
state :new
end
end
class Child
include NoBrainer::Document
include AASM
field :status, type: String
belongs_to :parent
aasm column: :status do
state :unknown_scope
state :new
end
end

View File

@ -0,0 +1,39 @@
class SilentPersistorNoBrainer
include NoBrainer::Document
include AASM
field :name
field :status
aasm :left, column: :status, whiny_persistence: false do
state :sleeping, initial: true
state :running
event :run do
transitions to: :running, from: :sleeping
end
event :sleep do
transitions to: :sleeping, from: :running
end
end
validates_presence_of :name
end
class MultipleSilentPersistorNoBrainer
include NoBrainer::Document
include AASM
field :name
field :status
aasm :left, column: :status, whiny_persistence: false do
state :sleeping, initial: true
state :running
event :run do
transitions to: :running, from: :sleeping
end
event :sleep do
transitions to: :sleeping, from: :running
end
end
validates_presence_of :name
end

View File

@ -0,0 +1,25 @@
class SimpleNewDslNoBrainer
include NoBrainer::Document
include AASM
field :status, type: String
aasm column: :status
aasm do
state :unknown_scope, initial: true
state :new
end
end
class SimpleNewDslNoBrainerMultiple
include NoBrainer::Document
include AASM
field :status, type: String
aasm :left, column: :status
aasm :left do
state :unknown_scope, initial: true
state :new
end
end

View File

@ -0,0 +1,23 @@
class SimpleNoBrainer
include NoBrainer::Document
include AASM
field :status, type: String
aasm column: :status do
state :unknown_scope, :another_unknown_scope
state :new
end
end
class SimpleNoBrainerMultiple
include NoBrainer::Document
include AASM
field :status, type: String
aasm :left, column: :status do
state :unknown_scope, :another_unknown_scope
state :new
end
end

View File

@ -0,0 +1,98 @@
class ValidatorNoBrainer
include NoBrainer::Document
include AASM
field :name
field :status
attr_accessor :invalid
validate do |_|
errors.add(:validator, 'invalid') if invalid
end
aasm column: :status, whiny_persistence: true do
before_all_transactions :before_all_transactions
after_all_transactions :after_all_transactions
state :sleeping, initial: true
state :running
state :failed, after_enter: :fail
event :run, after_commit: :change_name! do
transitions to: :running, from: :sleeping
end
event :sleep do
after_commit do |name|
change_name_on_sleep name
end
transitions to: :sleeping, from: :running
end
event :fail do
transitions to: :failed, from: %i[sleeping running]
end
end
validates_presence_of :name
def change_name!
self.name = 'name changed'
save!
end
def change_name_on_sleep(name)
self.name = name
save!
end
def fail
raise StandardError, 'failed on purpose'
end
end
class MultipleValidatorNoBrainer
include NoBrainer::Document
include AASM
field :name
field :status
attr_accessor :invalid
aasm :left, column: :status, whiny_persistence: true do
state :sleeping, initial: true
state :running
state :failed, after_enter: :fail
event :run, after_commit: :change_name! do
transitions to: :running, from: :sleeping
end
event :sleep do
after_commit do |name|
change_name_on_sleep name
end
transitions to: :sleeping, from: :running
end
event :fail do
transitions to: :failed, from: %i[sleeping running]
end
end
validates_presence_of :name
def change_name!
self.name = 'name changed'
save!
end
def change_name_on_sleep(name)
self.name = name
save!
end
def fail
raise StandardError, 'failed on purpose'
end
end

View File

@ -1,6 +1,7 @@
# encoding: utf-8
begin
require 'active_record'
puts "active_record gem found, running ActiveRecord specs \e[32m#{'✔'}\e[0m"
rescue LoadError
puts "active_record gem not found, not running ActiveRecord specs \e[31m#{'✖'}\e[0m"

View File

@ -1,4 +1,5 @@
# encoding: utf-8
begin
require 'dynamoid'
require 'aws-sdk-resources'
@ -7,14 +8,15 @@ begin
ENV['ACCESS_KEY'] ||= 'abcd'
ENV['SECRET_KEY'] ||= '1234'
Aws.config.update({
Aws.config.update(
region: 'us-west-2',
credentials: Aws::Credentials.new(ENV['ACCESS_KEY'], ENV['SECRET_KEY'])
})
)
Dynamoid.configure do |config|
config.namespace = "dynamoid_tests"
config.endpoint = 'http://127.0.0.1:30180'
config.namespace = 'dynamoid_tests'
config.endpoint = "http://#{ENV['DYNAMODB_HOST'] || '127.0.0.1'}:" \
"#{ENV['DYNAMODB_PORT'] || 30180}"
config.warn_on_scan = false
end

View File

@ -1,7 +1,26 @@
# encoding: utf-8
begin
require 'mongoid'
puts "mongoid gem found, running mongoid specs \e[32m#{'✔'}\e[0m"
if Mongoid::VERSION.to_f <= 5
Mongoid::Config.sessions = {
default: {
database: "mongoid_#{Process.pid}",
hosts: ["#{ENV['MONGODB_HOST'] || 'localhost'}:" \
"#{ENV['MONGODB_PORT'] || 27017}"]
}
}
else
Mongoid::Config.send(:clients=, {
default: {
database: "mongoid_#{Process.pid}",
hosts: ["#{ENV['MONGODB_HOST'] || 'localhost'}:" \
"#{ENV['MONGODB_PORT'] || 27017}"]
}
})
end
rescue LoadError
puts "mongoid gem not found, not running mongoid specs \e[31m#{'✖'}\e[0m"
end

View File

@ -0,0 +1,15 @@
# encoding: utf-8
begin
require 'nobrainer'
NoBrainer.configure do |config|
config.app_name = :aasm
config.environment = :test
config.warn_on_active_record = false
end
puts "nobrainer #{Gem.loaded_specs['nobrainer'].version} gem found, running nobrainer specs \e[32m#{'✔'}\e[0m"
rescue LoadError
puts "nobrainer gem not found, not running nobrainer specs \e[31m#{'✖'}\e[0m"
end

View File

@ -1,9 +1,12 @@
# encoding: utf-8
begin
require 'redis-objects'
require 'redis/objects/version'
puts "redis-objects gem found, running Redis specs \e[32m#{'✔'}\e[0m"
Redis.current = Redis.new(host: '127.0.0.1', port: 6379)
Redis.current = Redis.new(host: (ENV['REDIS_HOST'] || '127.0.0.1'),
port: (ENV['REDIS_PORT'] || 6379))
RSpec.configure do |c|
c.before(:each) do

View File

@ -10,10 +10,6 @@ if defined?(Mongoid::Document)
before(:all) do
# if you want to see the statements while running the spec enable the following line
# Mongoid.logger = Logger.new(STDERR)
Mongoid.configure do |config|
config.connect_to "mongoid_#{Process.pid}"
end
end
after do

View File

@ -10,10 +10,6 @@ if defined?(Mongoid::Document)
before(:all) do
# if you want to see the statements while running the spec enable the following line
# Mongoid.logger = Logger.new(STDERR)
Mongoid.configure do |config|
config.connect_to "mongoid_#{Process.pid}"
end
end
after do

View File

@ -0,0 +1,198 @@
require 'spec_helper'
if defined?(NoBrainer::Document)
describe 'nobrainer' do
Dir[File.dirname(__FILE__) + '/../../models/nobrainer/*.rb'].sort.each do |f|
require File.expand_path(f)
end
before(:all) do
# if you want to see the statements while running the spec enable the
# following line
# NoBrainer.configure do |config|
# config.logger = Logger.new(STDERR)
# end
end
after do
NoBrainer.purge!
end
describe 'named scopes with the old DSL' do
context 'Does not already respond_to? the scope name' do
it 'should add a scope for each state' do
expect(SimpleNoBrainerMultiple).to respond_to(:unknown_scope)
expect(SimpleNoBrainerMultiple).to respond_to(:another_unknown_scope)
expect(SimpleNoBrainerMultiple.unknown_scope.class).to eq(NoBrainer::Criteria)
expect(SimpleNoBrainerMultiple.another_unknown_scope.class).to eq(NoBrainer::Criteria)
end
end
context 'Already respond_to? the scope name' do
it 'should not add a scope' do
expect(SimpleNoBrainerMultiple).to respond_to(:new)
expect(SimpleNoBrainerMultiple.new.class).to eq(SimpleNoBrainerMultiple)
end
end
end
describe 'named scopes with the new DSL' do
context 'Does not already respond_to? the scope name' do
it 'should add a scope' do
expect(SimpleNewDslNoBrainerMultiple).to respond_to(:unknown_scope)
expect(SimpleNewDslNoBrainerMultiple.unknown_scope.class).to eq(NoBrainer::Criteria)
end
end
context 'Already respond_to? the scope name' do
it 'should not add a scope' do
expect(SimpleNewDslNoBrainerMultiple).to respond_to(:new)
expect(SimpleNewDslNoBrainerMultiple.new.class).to eq(SimpleNewDslNoBrainerMultiple)
end
end
it 'does not create scopes if requested' do
expect(NoScopeNoBrainerMultiple).not_to respond_to(:ignored_scope)
end
end
describe 'instance methods' do
let(:simple) { SimpleNewDslNoBrainerMultiple.new }
it 'should initialize the aasm state on instantiation' do
expect(SimpleNewDslNoBrainerMultiple.new.status).to eql 'unknown_scope'
expect(SimpleNewDslNoBrainerMultiple.new.aasm(:left).current_state).to eql :unknown_scope
end
end
describe 'transitions with persistence' do
it 'should work for valid models' do
valid_object = MultipleValidatorNoBrainer.create(name: 'name')
expect(valid_object).to be_sleeping
valid_object.status = :running
expect(valid_object).to be_running
end
it 'should not store states for invalid models' do
validator = MultipleValidatorNoBrainer.create(name: 'name')
expect(validator).to be_valid
expect(validator).to be_sleeping
validator.name = nil
expect(validator).not_to be_valid
expect { validator.run! }.to raise_error(NoBrainer::Error::DocumentInvalid)
expect(validator).to be_sleeping
validator.reload
expect(validator).not_to be_running
expect(validator).to be_sleeping
validator.name = 'another name'
expect(validator).to be_valid
expect(validator.run!).to be_truthy
expect(validator).to be_running
validator.reload
expect(validator).to be_running
expect(validator).not_to be_sleeping
end
it 'should not store states for invalid models silently if configured' do
validator = MultipleSilentPersistorNoBrainer.create(name: 'name')
expect(validator).to be_valid
expect(validator).to be_sleeping
validator.name = nil
expect(validator).not_to be_valid
expect(validator.run!).to be_falsey
expect(validator).to be_sleeping
validator.reload
expect(validator).not_to be_running
expect(validator).to be_sleeping
validator.name = 'another name'
expect(validator).to be_valid
expect(validator.run!).to be_truthy
expect(validator).to be_running
validator.reload
expect(validator).to be_running
expect(validator).not_to be_sleeping
end
it 'should store states for invalid models if configured' do
persistor = MultipleInvalidPersistorNoBrainer.create(name: 'name')
expect(persistor).to be_valid
expect(persistor).to be_sleeping
persistor.name = nil
expect(persistor).not_to be_valid
expect(persistor.run!).to be_truthy
expect(persistor).to be_running
persistor = MultipleInvalidPersistorNoBrainer.find(persistor.id)
persistor.valid?
expect(persistor).to be_valid
expect(persistor).to be_running
expect(persistor).not_to be_sleeping
persistor.reload
expect(persistor).to be_running
expect(persistor).not_to be_sleeping
end
end
describe 'complex example' do
it 'works' do
record = ComplexNoBrainerExample.new
expect_aasm_states record, :one, :alpha
record.save!
expect_aasm_states record, :one, :alpha
record.reload
expect_aasm_states record, :one, :alpha
record.increment!
expect_aasm_states record, :two, :alpha
record.reload
expect_aasm_states record, :two, :alpha
record.level_up!
expect_aasm_states record, :two, :beta
record.reload
expect_aasm_states record, :two, :beta
record.increment!
expect { record.increment! }.to raise_error(AASM::InvalidTransition)
expect_aasm_states record, :three, :beta
record.reload
expect_aasm_states record, :three, :beta
record.level_up!
expect_aasm_states record, :three, :gamma
record.reload
expect_aasm_states record, :three, :gamma
record.level_down # without saving
expect_aasm_states record, :three, :beta
record.reload
expect_aasm_states record, :three, :gamma
record.level_down # without saving
expect_aasm_states record, :three, :beta
record.reset!
expect_aasm_states record, :one, :beta
end
def expect_aasm_states(record, left_state, right_state)
expect(record.aasm(:left).current_state).to eql left_state.to_sym
expect(record.left).to eql left_state.to_s
expect(record.aasm(:right).current_state).to eql right_state.to_sym
expect(record.right).to eql right_state.to_s
end
end
end
end

View File

@ -0,0 +1,158 @@
require 'spec_helper'
if defined?(NoBrainer::Document)
describe 'nobrainer' do
Dir[File.dirname(__FILE__) + '/../../models/nobrainer/*.rb'].sort.each do |f|
require File.expand_path(f)
end
before(:all) do
# if you want to see the statements while running the spec enable the
# following line
# NoBrainer.configure do |config|
# config.logger = Logger.new(STDERR)
# end
end
after do
NoBrainer.purge!
end
describe 'named scopes with the old DSL' do
context 'Does not already respond_to? the scope name' do
it 'should add a scope for each state' do
expect(SimpleNoBrainer).to respond_to(:unknown_scope)
expect(SimpleNoBrainer).to respond_to(:another_unknown_scope)
expect(SimpleNoBrainer.unknown_scope.class).to eq(NoBrainer::Criteria)
expect(SimpleNoBrainer.another_unknown_scope.class).to eq(NoBrainer::Criteria)
end
end
context 'Already respond_to? the scope name' do
it 'should not add a scope' do
expect(SimpleNoBrainer).to respond_to(:new)
expect(SimpleNoBrainer.new.class).to eq(SimpleNoBrainer)
end
end
end
describe 'named scopes with the new DSL' do
context 'Does not already respond_to? the scope name' do
it 'should add a scope' do
expect(SimpleNewDslNoBrainer).to respond_to(:unknown_scope)
expect(SimpleNewDslNoBrainer.unknown_scope.class).to eq(NoBrainer::Criteria)
end
end
context 'Already respond_to? the scope name' do
it 'should not add a scope' do
expect(SimpleNewDslNoBrainer).to respond_to(:new)
expect(SimpleNewDslNoBrainer.new.class).to eq(SimpleNewDslNoBrainer)
end
end
it 'does not create scopes if requested' do
expect(NoScopeNoBrainer).not_to respond_to(:ignored_scope)
end
end
describe 'instance methods' do
let(:simple) { SimpleNewDslNoBrainer.new }
it 'should initialize the aasm state on instantiation' do
expect(SimpleNewDslNoBrainer.new.status).to eql 'unknown_scope'
expect(SimpleNewDslNoBrainer.new.aasm.current_state).to eql :unknown_scope
end
end
describe 'relations object' do
it 'should load relations object ids' do
parent = Parent.create
child_1 = Child.create(parent_id: parent.id)
child_2 = Child.create(parent_id: parent.id)
expect(parent.childs.pluck(:id, :status).map(&:id)).to eql [child_1.id, child_2.id]
end
end
describe 'transitions with persistence' do
it 'should work for valid models' do
valid_object = ValidatorNoBrainer.create(name: 'name')
expect(valid_object).to be_sleeping
valid_object.status = :running
expect(valid_object).to be_running
end
it 'should not store states for invalid models' do
validator = ValidatorNoBrainer.create(name: 'name')
expect(validator).to be_valid
expect(validator).to be_sleeping
validator.name = nil
expect(validator).not_to be_valid
expect { validator.run! }.to raise_error(NoBrainer::Error::DocumentInvalid)
expect(validator).to be_sleeping
validator.reload
expect(validator).not_to be_running
expect(validator).to be_sleeping
validator.name = 'another name'
expect(validator).to be_valid
expect(validator.run!).to be_truthy
expect(validator).to be_running
validator.reload
expect(validator).to be_running
expect(validator).not_to be_sleeping
end
it 'should not store states for invalid models silently if configured' do
validator = SilentPersistorNoBrainer.create(name: 'name')
expect(validator).to be_valid
expect(validator).to be_sleeping
validator.name = nil
expect(validator).not_to be_valid
expect(validator.run!).to be_falsey
expect(validator).to be_sleeping
validator.reload
expect(validator).not_to be_running
expect(validator).to be_sleeping
validator.name = 'another name'
expect(validator).to be_valid
expect(validator.run!).to be_truthy
expect(validator).to be_running
validator.reload
expect(validator).to be_running
expect(validator).not_to be_sleeping
end
it 'should store states for invalid models if configured' do
persistor = InvalidPersistorNoBrainer.create(name: 'name')
expect(persistor).to be_valid
expect(persistor).to be_sleeping
persistor.name = nil
expect(persistor).not_to be_valid
expect(persistor.run!).to be_truthy
expect(persistor).to be_running
persistor = InvalidPersistorNoBrainer.find(persistor.id)
persistor.valid?
expect(persistor).to be_valid
expect(persistor).to be_running
expect(persistor).not_to be_sleeping
persistor.reload
expect(persistor).to be_running
expect(persistor).not_to be_sleeping
end
end
end
end

View File

@ -34,8 +34,8 @@ begin
})
Dynamoid.configure do |config|
config.namespace = "dynamoid_tests"
config.endpoint = 'http://127.0.0.1:30180'
config.namespace = 'dynamoid_tests'
config.endpoint = "http://#{ENV['DYNAMODB_HOST'] || '127.0.0.1'}:30180"
config.warn_on_scan = false
end