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 - mongodb
- redis-server - redis-server
addons:
rethinkdb: '2.3.6'
gemfile: gemfile:
- gemfiles/rails_3.2.gemfile - gemfiles/rails_3.2.gemfile
- gemfiles/rails_4.0.gemfile - gemfiles/rails_4.0.gemfile
- gemfiles/rails_4.2.gemfile - gemfiles/rails_4.2.gemfile
- gemfiles/rails_4.2_mongoid_5.gemfile - gemfiles/rails_4.2_mongoid_5.gemfile
- gemfiles/rails_4.2_nobrainer.gemfile
- gemfiles/rails_5.0.gemfile - gemfiles/rails_5.0.gemfile
- gemfiles/rails_5.0_nobrainer.gemfile
before_script: before_script:
- mkdir /tmp/dynamodb - mkdir /tmp/dynamodb

View File

@ -1,47 +1,58 @@
appraise 'rails_3.2' do appraise 'rails_3.2' do
gem 'rails', '~>3.2.22' gem 'rails', '~> 3.2.22'
gem 'mongoid', '~>3.1' gem 'mongoid', '~> 3.1'
gem 'sequel' gem 'sequel'
gem 'bson_ext', :platforms => :ruby gem 'bson_ext', platforms: :ruby
gem 'test-unit', '~> 3.0' gem 'test-unit', '~> 3.0'
gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', :platforms => :jruby gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', platforms: :jruby
end end
appraise 'rails_4.0' do 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 'rails', '4.0.13'
gem 'mongoid', '~>4.0' gem 'mongoid', '~> 4.0'
gem 'sequel' gem 'sequel'
gem 'dynamoid', '~> 1', :platforms => :ruby gem 'dynamoid', '~> 1', platforms: :ruby
gem 'aws-sdk', '~>2', :platforms => :ruby gem 'aws-sdk', '~> 2', platforms: :ruby
gem 'redis-objects' gem 'redis-objects'
gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', :platforms => :jruby gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', platforms: :jruby
end end
appraise 'rails_4.2' do appraise 'rails_4.2' do
gem 'nokogiri', '1.6.8.1', :platforms => [:ruby_19] gem 'nokogiri', '1.6.8.1', platforms: %i[ruby_19]
gem 'mime-types', '~> 2', :platforms => [:ruby_19, :jruby] gem 'mime-types', '~> 2', platforms: %i[ruby_19 jruby]
gem 'rails', '4.2.5' gem 'rails', '4.2.5'
gem 'mongoid', '~>4.0' gem 'mongoid', '~> 4.0'
gem 'sequel' gem 'sequel'
gem 'dynamoid', '~> 1', :platforms => :ruby gem 'dynamoid', '~> 1', platforms: :ruby
gem 'aws-sdk', '~>2', :platforms => :ruby gem 'aws-sdk', '~> 2', platforms: :ruby
gem 'redis-objects' 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 end
appraise 'rails_4.2_mongoid_5' do 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 'rails', '4.2.5'
gem 'mongoid', '~>5.0' gem 'mongoid', '~> 5.0'
gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', :platforms => :jruby gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', platforms: :jruby
end end
appraise 'rails_5.0' do appraise 'rails_5.0' do
gem 'rails', '5.0.0' gem 'rails', '5.0.0'
gem 'mongoid', '~>6.0' gem 'mongoid', '~> 6.0'
gem 'sequel' gem 'sequel'
gem 'dynamoid', '~> 1', :platforms => :ruby gem 'dynamoid', '~> 1', platforms: :ruby
gem 'aws-sdk', '~>2', :platforms => :ruby gem 'aws-sdk', '~> 2', platforms: :ruby
gem 'redis-objects' gem 'redis-objects'
end 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 # CHANGELOG
## unreleased ## unreleased
* Add support for Nobrainer (RethinkDB)
## 4.12.3 ## 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) * 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) - [Sequel](#sequel)
- [Dynamoid](#dynamoid) - [Dynamoid](#dynamoid)
- [Mongoid](#mongoid) - [Mongoid](#mongoid)
- [Nobrainer](#nobrainer)
- [Redis](#redis) - [Redis](#redis)
- [Automatic Scopes](#automatic-scopes) - [Automatic Scopes](#automatic-scopes)
- [Transaction support](#transaction-support) - [Transaction support](#transaction-support)
@ -52,10 +53,8 @@
This package contains AASM, a library for adding finite state machines to Ruby classes. 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 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 that no longer targets only ActiveRecord models. It currently provides adapters for many
[ActiveRecord](http://api.rubyonrails.org/classes/ActiveRecord/Base.html), ORMs but it can be used for any Ruby class, no matter what parent class it has (if any).
and [Mongoid](http://mongoid.org/) but it can be used for any Ruby class, no matter what
parent class it has (if any).
## Upgrade from version 3 to 4 ## Upgrade from version 3 to 4
@ -825,6 +824,23 @@ class Job
end 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 ### Redis
AASM also supports persistence in Redis via 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") elsif hierarchy.include?("Mongoid::Document")
require_persistence :mongoid require_persistence :mongoid
include_persistence base, :mongoid include_persistence base, :mongoid
elsif hierarchy.include?("NoBrainer::Document")
require_persistence :no_brainer
include_persistence base, :no_brainer
elsif hierarchy.include?("Sequel::Model") elsif hierarchy.include?("Sequel::Model")
require_persistence :sequel require_persistence :sequel
include_persistence base, :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/active_record_persistence.rb',
'aasm/persistence/dynamoid_persistence.rb', 'aasm/persistence/dynamoid_persistence.rb',
'aasm/persistence/mongoid_persistence.rb', 'aasm/persistence/mongoid_persistence.rb',
'aasm/persistence/no_brainer_persistence.rb',
'aasm/persistence/sequel_persistence.rb', 'aasm/persistence/sequel_persistence.rb',
'aasm/persistence/redis_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 # encoding: utf-8
begin begin
require 'active_record' require 'active_record'
puts "active_record gem found, running ActiveRecord specs \e[32m#{'✔'}\e[0m" puts "active_record gem found, running ActiveRecord specs \e[32m#{'✔'}\e[0m"
rescue LoadError rescue LoadError
puts "active_record gem not found, not running ActiveRecord specs \e[31m#{'✖'}\e[0m" puts "active_record gem not found, not running ActiveRecord specs \e[31m#{'✖'}\e[0m"

View File

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

View File

@ -1,7 +1,26 @@
# encoding: utf-8 # encoding: utf-8
begin begin
require 'mongoid' require 'mongoid'
puts "mongoid gem found, running mongoid specs \e[32m#{'✔'}\e[0m" 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 rescue LoadError
puts "mongoid gem not found, not running mongoid specs \e[31m#{'✖'}\e[0m" puts "mongoid gem not found, not running mongoid specs \e[31m#{'✖'}\e[0m"
end 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 # encoding: utf-8
begin begin
require 'redis-objects' require 'redis-objects'
require 'redis/objects/version'
puts "redis-objects gem found, running Redis specs \e[32m#{'✔'}\e[0m" 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| RSpec.configure do |c|
c.before(:each) do c.before(:each) do

View File

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

View File

@ -10,10 +10,6 @@ if defined?(Mongoid::Document)
before(:all) do before(:all) do
# if you want to see the statements while running the spec enable the following line # if you want to see the statements while running the spec enable the following line
# Mongoid.logger = Logger.new(STDERR) # Mongoid.logger = Logger.new(STDERR)
Mongoid.configure do |config|
config.connect_to "mongoid_#{Process.pid}"
end
end end
after do 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| Dynamoid.configure do |config|
config.namespace = "dynamoid_tests" config.namespace = 'dynamoid_tests'
config.endpoint = 'http://127.0.0.1:30180' config.endpoint = "http://#{ENV['DYNAMODB_HOST'] || '127.0.0.1'}:30180"
config.warn_on_scan = false config.warn_on_scan = false
end end