Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

- first working version

  • Loading branch information...
commit 9e9294cf731614de5220751057c764af62472f29 1 parent 3492b79
@martinciu authored
View
3  Gemfile
@@ -1,8 +1,9 @@
source :rubygems
+gem "bson_ext", "1.0.1"
gem "mongoid", ">=2.0.0.beta7"
gem "state_machine", ">=0.9.2"
gem "activemodel", ">=3.0.0.beta4", :require => "active_model"
group :development do
- gem "test-unit"
+ gem "rspec"
end
View
31 Rakefile
@@ -7,41 +7,32 @@ begin
Jeweler::Tasks.new do |gem|
gem.name = "state_machine-mongoid"
gem.summary = %Q{state_machine mongoid integration}
- gem.description = %Q{TODO: longer description of your gem}
+ gem.description = %Q{a little lack of tests but it works!}
gem.email = "marcin.ciunelis@gmail.com"
gem.homepage = "http://github.com/martinciu/state_machine-mongoid"
gem.authors = ["Marcin Ciunelis"]
gem.add_bundler_dependencies
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
end
Jeweler::GemcutterTasks.new
rescue LoadError
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
end
-require 'rake/testtask'
-Rake::TestTask.new(:test) do |test|
- test.libs << 'lib' << 'test'
- test.pattern = 'test/**/test_*.rb'
- test.verbose = true
+require 'spec/rake/spectask'
+Spec::Rake::SpecTask.new(:spec) do |spec|
+ spec.libs << 'lib' << 'spec'
+ spec.spec_files = FileList['spec/**/*_spec.rb']
end
-begin
- require 'rcov/rcovtask'
- Rcov::RcovTask.new do |test|
- test.libs << 'test'
- test.pattern = 'test/**/test_*.rb'
- test.verbose = true
- end
-rescue LoadError
- task :rcov do
- abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
- end
+Spec::Rake::SpecTask.new(:rcov) do |spec|
+ spec.libs << 'lib' << 'spec'
+ spec.pattern = 'spec/**/*_spec.rb'
+ spec.rcov = true
end
-task :test => :check_dependencies
+task :spec => :check_dependencies
-task :default => :test
+task :default => :spec
require 'rake/rdoctask'
Rake::RDocTask.new do |rdoc|
View
582 lib/state_machine/integrations/mongoid.rb
@@ -1,295 +1,321 @@
module StateMachine
- module Integrations #:nodoc:
- # Adds support for integrating state machines with Mongoid models.
- #
- # == Examples
- #
- # Below is an example of a simple state machine defined within a
- # Mongoid model:
- #
- # class Vehicle
- # include Mongoid::Document
- #
- # state_machine :initial => :parked do
- # event :ignite do
- # transition :parked => :idling
- # end
- # end
- # end
- #
- # The examples in the sections below will use the above class as a
- # reference.
- #
- # == Actions
- #
- # By default, the action that will be invoked when a state is transitioned
- # is the +save+ action. This will cause the record to save the changes
- # made to the state machine's attribute. *Note* that if any other changes
- # were made to the record prior to transition, then those changes will
- # be saved as well.
- #
- # For example,
- #
- # vehicle = Vehicle.create # => #<Vehicle id: 1, name: nil, state: "parked">
- # vehicle.name = 'Ford Explorer'
- # vehicle.ignite # => true
- # vehicle.reload # => #<Vehicle id: 1, name: "Ford Explorer", state: "idling">
- #
- # == Events
- #
- # As described in StateMachine::InstanceMethods#state_machine, event
- # attributes are created for every machine that allow transitions to be
- # performed automatically when the object's action (in this case, :save)
- # is called.
- #
- # In Mongoid, these automated events are run in the following order:
- # * before validation - Run before callbacks and persist new states, then validate
- # * before save - If validation was skipped, run before callbacks and persist new states, then save
- # * after save - Run after callbacks
- #
- # For example,
- #
- # vehicle = Vehicle.create # => #<Vehicle id: 1, name: nil, state: "parked">
- # vehicle.state_event # => nil
- # vehicle.state_event = 'invalid'
- # vehicle.valid? # => false
- # vehicle.errors.full_messages # => ["State event is invalid"]
- #
- # vehicle.state_event = 'ignite'
- # vehicle.valid? # => true
- # vehicle.save # => true
- # vehicle.state # => "idling"
- # vehicle.state_event # => nil
- #
- # Note that this can also be done on a mass-assignment basis:
- #
- # vehicle = Vehicle.create(:state_event => 'ignite') # => #<Vehicle id: 1, name: nil, state: "idling">
- # vehicle.state # => "idling"
- #
- # This technique is always used for transitioning states when the +save+
- # action (which is the default) is configured for the machine.
- #
- # === Security implications
- #
- # Beware that public event attributes mean that events can be fired
- # whenever mass-assignment is being used. If you want to prevent malicious
- # users from tampering with events through URLs / forms, the attribute
- # should be protected like so:
- #
- # class Vehicle
- # include Mongoid::Document
- #
- # attr_protected :state_event
- # # attr_accessible ... # Alternative technique
- #
- # state_machine do
- # ...
- # end
- # end
- #
- # If you want to only have *some* events be able to fire via mass-assignment,
- # you can build two state machines (one public and one protected) like so:
- #
- # class Vehicle
- # include Mongoid::Document
- #
- # attr_protected :state_event # Prevent access to events in the first machine
- #
- # state_machine do
- # # Define private events here
- # end
- #
- # # Public machine targets the same state as the private machine
- # state_machine :public_state, :attribute => :state do
- # # Define public events here
- # end
- # end
- #
- # == Validation errors
- #
- # If an event fails to successfully fire because there are no matching
- # transitions for the current record, a validation error is added to the
- # record's state attribute to help in determining why it failed and for
- # reporting via the UI.
- #
- # For example,
- #
- # vehicle = Vehicle.create(:state => 'idling') # => #<Vehicle id: 1, name: nil, state: "idling">
- # vehicle.ignite # => false
- # vehicle.errors.full_messages # => ["State cannot transition via \"ignite\""]
- #
- # If an event fails to fire because of a validation error on the record and
- # *not* because a matching transition was not available, no error messages
- # will be added to the state attribute.
- #
- # == Scopes
- #
- # To assist in filtering models with specific states, a series of basic
- # scopes are defined on the model for finding records with or without a
- # particular set of states.
- #
- # These scopes are essentially the functional equivalent of the following
- # definitions:
- #
- # class Vehicle
- # include Mongoid::Document
- #
- # def self.with_states(*states)
- # all(:conditions => {:state => {'$in' => states}})
- # end
- # # with_states also aliased to with_state
- #
- # def self.without_states(*states)
- # all(:conditions => {:state => {'$nin' => states}})
- # end
- # # without_states also aliased to without_state
- # end
- #
- # *Note*, however, that the states are converted to their stored values
- # before being passed into the query.
- #
- # Because of the way named scopes work in Mongoid, they *cannot* be
- # chained.
- #
- # == Callbacks
- #
- # All before/after transition callbacks defined for Mongoid models
- # behave in the same way that other Mongoid callbacks behave. The
- # object involved in the transition is passed in as an argument.
- #
- # For example,
- #
- # class Vehicle
- # include Mongoid::Document
- #
- # state_machine :initial => :parked do
- # before_transition any => :idling do |vehicle|
- # vehicle.put_on_seatbelt
- # end
- #
- # before_transition do |vehicle, transition|
- # # log message
- # end
- #
- # event :ignite do
- # transition :parked => :idling
- # end
- # end
- #
- # def put_on_seatbelt
- # ...
- # end
- # end
- #
- # Note, also, that the transition can be accessed by simply defining
- # additional arguments in the callback block.
+ module Integrations
module Mongoid
- include ActiveModel
-
# The default options to use for state machines using this integration
- @defaults = {:action => :save}
-
+ class << self; attr_reader :defaults; end
+ @defaults = {:action => :save, :use_transactions => false}
+
# Should this integration be used for state machines in the given class?
- # Classes that include Mongoid::Document will automatically use the
+ # Classes that include Mongoid::Resource will automatically use the
# Mongoid integration.
def self.matches?(klass)
defined?(::Mongoid::Document) && klass <= ::Mongoid::Document
end
-
- # Adds a validation error to the given object (no i18n support)
- def invalidate(object, attribute, message, values = [])
- object.errors.add(self.attribute(attribute), generate_message(message, values))
- end
-
+
protected
- # Does not support observers
- def supports_observers?
- false
- end
-
- # Always adds validation support
- def supports_validations?
- true
- end
-
- # Only runs validations on the action if using <tt>:save</tt>
- def runs_validations_on_action?
- action == :save
- end
-
- # Always adds dirty tracking support
- def supports_dirty_tracking?(object)
- true
- end
-
- # Don't allow callback terminators
- def callback_terminator
- end
-
+
# Defines an initialization hook into the owner class for setting the
# initial state of the machine *before* any attributes are set on the
# object
def define_state_initializer
- @instance_helper_module.class_eval <<-end_eval, __FILE__, __LINE__
- def initialize(attrs = {}, *args)
- from_database = args.first
-
- if !from_database && (!attrs || !attrs.stringify_keys.key?('_id'))
- filtered = respond_to?(:filter_protected_attrs) ? filter_protected_attrs(attrs) : attrs
- ignore = filtered ? filtered.keys : []
-
- initialize_state_machines(:dynamic => false, :ignore => ignore)
- super
- initialize_state_machines(:dynamic => true, :ignore => ignore)
- else
- super
- end
- end
- end_eval
- end
-
- # Skips defining reader/writer methods since this is done automatically
- def define_state_accessor
- owner_class.key(attribute, String) unless owner_class.keys.include?(attribute)
-
- name = self.name
- owner_class.validates_each(attribute, :logic => lambda {|*|
- machine = self.class.state_machine(name)
- machine.invalidate(self, :state, :invalid) unless machine.states.match(self)
- })
- end
-
- # Adds support for defining the attribute predicate, while providing
- # compatibility with the default predicate which determines whether
- # *anything* is set for the attribute's value
- def define_state_predicate
- name = self.name
-
- # Still use class_eval here instance of define_instance_method since
- # we need to be able to call +super+
- @instance_helper_module.class_eval do
- define_method("#{name}?") do |*args|
- args.empty? ? super(*args) : self.class.state_machine(name).states.matches?(self, *args)
- end
- end
- end
-
- # Adds hooks into validation for automatically firing events
- def define_action_helpers
- super(action == :save ? :create_or_update : action)
- end
-
- # Creates a scope for finding records *with* a particular state or
- # states for the attribute
- def create_with_scope(name)
- lambda {|model, values| model.all(:conditions => {attribute => {'$in' => values}})}
- end
-
- # Creates a scope for finding records *without* a particular state or
- # states for the attribute
- def create_without_scope(name)
- lambda {|model, values| model.all(:conditions => {attribute => {'$nin' => values}})}
+
end
+
end
end
end
+# module StateMachine
+# module Integrations #:nodoc:
+# # Adds support for integrating state machines with Mongoid models.
+# #
+# # == Examples
+# #
+# # Below is an example of a simple state machine defined within a
+# # Mongoid model:
+# #
+# # class Vehicle
+# # include Mongoid::Document
+# #
+# # state_machine :initial => :parked do
+# # event :ignite do
+# # transition :parked => :idling
+# # end
+# # end
+# # end
+# #
+# # The examples in the sections below will use the above class as a
+# # reference.
+# #
+# # == Actions
+# #
+# # By default, the action that will be invoked when a state is transitioned
+# # is the +save+ action. This will cause the record to save the changes
+# # made to the state machine's attribute. *Note* that if any other changes
+# # were made to the record prior to transition, then those changes will
+# # be saved as well.
+# #
+# # For example,
+# #
+# # vehicle = Vehicle.create # => #<Vehicle id: 1, name: nil, state: "parked">
+# # vehicle.name = 'Ford Explorer'
+# # vehicle.ignite # => true
+# # vehicle.reload # => #<Vehicle id: 1, name: "Ford Explorer", state: "idling">
+# #
+# # == Events
+# #
+# # As described in StateMachine::InstanceMethods#state_machine, event
+# # attributes are created for every machine that allow transitions to be
+# # performed automatically when the object's action (in this case, :save)
+# # is called.
+# #
+# # In Mongoid, these automated events are run in the following order:
+# # * before validation - Run before callbacks and persist new states, then validate
+# # * before save - If validation was skipped, run before callbacks and persist new states, then save
+# # * after save - Run after callbacks
+# #
+# # For example,
+# #
+# # vehicle = Vehicle.create # => #<Vehicle id: 1, name: nil, state: "parked">
+# # vehicle.state_event # => nil
+# # vehicle.state_event = 'invalid'
+# # vehicle.valid? # => false
+# # vehicle.errors.full_messages # => ["State event is invalid"]
+# #
+# # vehicle.state_event = 'ignite'
+# # vehicle.valid? # => true
+# # vehicle.save # => true
+# # vehicle.state # => "idling"
+# # vehicle.state_event # => nil
+# #
+# # Note that this can also be done on a mass-assignment basis:
+# #
+# # vehicle = Vehicle.create(:state_event => 'ignite') # => #<Vehicle id: 1, name: nil, state: "idling">
+# # vehicle.state # => "idling"
+# #
+# # This technique is always used for transitioning states when the +save+
+# # action (which is the default) is configured for the machine.
+# #
+# # === Security implications
+# #
+# # Beware that public event attributes mean that events can be fired
+# # whenever mass-assignment is being used. If you want to prevent malicious
+# # users from tampering with events through URLs / forms, the attribute
+# # should be protected like so:
+# #
+# # class Vehicle
+# # include Mongoid::Document
+# #
+# # attr_protected :state_event
+# # # attr_accessible ... # Alternative technique
+# #
+# # state_machine do
+# # ...
+# # end
+# # end
+# #
+# # If you want to only have *some* events be able to fire via mass-assignment,
+# # you can build two state machines (one public and one protected) like so:
+# #
+# # class Vehicle
+# # include Mongoid::Document
+# #
+# # attr_protected :state_event # Prevent access to events in the first machine
+# #
+# # state_machine do
+# # # Define private events here
+# # end
+# #
+# # # Public machine targets the same state as the private machine
+# # state_machine :public_state, :attribute => :state do
+# # # Define public events here
+# # end
+# # end
+# #
+# # == Validation errors
+# #
+# # If an event fails to successfully fire because there are no matching
+# # transitions for the current record, a validation error is added to the
+# # record's state attribute to help in determining why it failed and for
+# # reporting via the UI.
+# #
+# # For example,
+# #
+# # vehicle = Vehicle.create(:state => 'idling') # => #<Vehicle id: 1, name: nil, state: "idling">
+# # vehicle.ignite # => false
+# # vehicle.errors.full_messages # => ["State cannot transition via \"ignite\""]
+# #
+# # If an event fails to fire because of a validation error on the record and
+# # *not* because a matching transition was not available, no error messages
+# # will be added to the state attribute.
+# #
+# # == Scopes
+# #
+# # To assist in filtering models with specific states, a series of basic
+# # scopes are defined on the model for finding records with or without a
+# # particular set of states.
+# #
+# # These scopes are essentially the functional equivalent of the following
+# # definitions:
+# #
+# # class Vehicle
+# # include Mongoid::Document
+# #
+# # def self.with_states(*states)
+# # all(:conditions => {:state => {'$in' => states}})
+# # end
+# # # with_states also aliased to with_state
+# #
+# # def self.without_states(*states)
+# # all(:conditions => {:state => {'$nin' => states}})
+# # end
+# # # without_states also aliased to without_state
+# # end
+# #
+# # *Note*, however, that the states are converted to their stored values
+# # before being passed into the query.
+# #
+# # Because of the way named scopes work in Mongoid, they *cannot* be
+# # chained.
+# #
+# # == Callbacks
+# #
+# # All before/after transition callbacks defined for Mongoid models
+# # behave in the same way that other Mongoid callbacks behave. The
+# # object involved in the transition is passed in as an argument.
+# #
+# # For example,
+# #
+# # class Vehicle
+# # include Mongoid::Document
+# #
+# # state_machine :initial => :parked do
+# # before_transition any => :idling do |vehicle|
+# # vehicle.put_on_seatbelt
+# # end
+# #
+# # before_transition do |vehicle, transition|
+# # # log message
+# # end
+# #
+# # event :ignite do
+# # transition :parked => :idling
+# # end
+# # end
+# #
+# # def put_on_seatbelt
+# # ...
+# # end
+# # end
+# #
+# # Note, also, that the transition can be accessed by simply defining
+# # additional arguments in the callback block.
+# module Mongoid
+# include ActiveModel
+#
+# # The default options to use for state machines using this integration
+# @defaults = {:action => :save}
+#
+# # Should this integration be used for state machines in the given class?
+# # Classes that include Mongoid::Document will automatically use the
+# # Mongoid integration.
+# def self.matches?(klass)
+# defined?(::Mongoid::Document) && klass <= ::Mongoid::Document
+# end
+#
+# # Adds a validation error to the given object (no i18n support)
+# def invalidate(object, attribute, message, values = [])
+# object.errors.add(self.attribute(attribute), generate_message(message, values))
+# end
+#
+# protected
+# # Does not support observers
+# def supports_observers?
+# false
+# end
+#
+# # Always adds validation support
+# def supports_validations?
+# true
+# end
+#
+# # Only runs validations on the action if using <tt>:save</tt>
+# def runs_validations_on_action?
+# action == :save
+# end
+#
+# # Always adds dirty tracking support
+# def supports_dirty_tracking?(object)
+# true
+# end
+#
+# # Don't allow callback terminators
+# def callback_terminator
+# end
+#
+# # Defines an initialization hook into the owner class for setting the
+# # initial state of the machine *before* any attributes are set on the
+# # object
+# def define_state_initializer
+# @instance_helper_module.class_eval <<-end_eval, __FILE__, __LINE__
+# def initialize(attrs = {}, *args)
+# from_database = args.first
+#
+# if !from_database && (!attrs || !attrs.stringify_keys.key?('_id'))
+# filtered = respond_to?(:filter_protected_attrs) ? filter_protected_attrs(attrs) : attrs
+# ignore = filtered ? filtered.keys : []
+#
+# initialize_state_machines(:dynamic => false, :ignore => ignore)
+# super
+# initialize_state_machines(:dynamic => true, :ignore => ignore)
+# else
+# super
+# end
+# end
+# end_eval
+# end
+#
+# # Skips defining reader/writer methods since this is done automatically
+# def define_state_accessor
+# owner_class.field(attribute, :type => String) unless owner_class.fields.nil? #.include?(attribute)
+#
+# name = self.name
+# owner_class.validates_each(attribute, :logic => lambda {|*|
+# machine = self.class.state_machine(name)
+# machine.invalidate(self, :state, :invalid) unless machine.states.match(self)
+# })
+# end
+#
+# # Adds support for defining the attribute predicate, while providing
+# # compatibility with the default predicate which determines whether
+# # *anything* is set for the attribute's value
+# def define_state_predicate
+# name = self.name
+#
+# # Still use class_eval here instance of define_instance_method since
+# # we need to be able to call +super+
+# @instance_helper_module.class_eval do
+# define_method("#{name}?") do |*args|
+# args.empty? ? super(*args) : self.class.state_machine(name).states.matches?(self, *args)
+# end
+# end
+# end
+#
+# # Adds hooks into validation for automatically firing events
+# def define_action_helpers
+# super(action == :save ? :create_or_update : action)
+# end
+#
+# # Creates a scope for finding records *with* a particular state or
+# # states for the attribute
+# def create_with_scope(name)
+# lambda {|model, values| model.all(:conditions => {attribute => {'$in' => values}})}
+# end
+#
+# # Creates a scope for finding records *without* a particular state or
+# # states for the attribute
+# def create_without_scope(name)
+# lambda {|model, values| model.all(:conditions => {attribute => {'$nin' => values}})}
+# end
+# end
+# end
+# end
View
27 state_machine-mongoid.gemspec
@@ -5,12 +5,12 @@
Gem::Specification.new do |s|
s.name = %q{state_machine-mongoid}
- s.version = "0.0.0"
+ s.version = "0.1.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Marcin Ciunelis"]
s.date = %q{2010-06-27}
- s.description = %q{TODO: longer description of your gem}
+ s.description = %q{a little lack of tests but it works!}
s.email = %q{marcin.ciunelis@gmail.com}
s.extra_rdoc_files = [
"LICENSE",
@@ -19,13 +19,18 @@ Gem::Specification.new do |s|
s.files = [
".document",
".gitignore",
+ "Gemfile",
"LICENSE",
"README.rdoc",
"Rakefile",
"VERSION",
"lib/state_machine-mongoid.rb",
- "test/helper.rb",
- "test/test_state_machine-mongoid.rb"
+ "lib/state_machine/integrations/mongoid.rb",
+ "spec/spec.opts",
+ "spec/spec_helper.rb",
+ "spec/state_machine-mongoid_spec.rb",
+ "spec/vehicle.rb",
+ "state_machine-mongoid.gemspec"
]
s.homepage = %q{http://github.com/martinciu/state_machine-mongoid}
s.rdoc_options = ["--charset=UTF-8"]
@@ -33,8 +38,9 @@ Gem::Specification.new do |s|
s.rubygems_version = %q{1.3.7}
s.summary = %q{state_machine mongoid integration}
s.test_files = [
- "test/helper.rb",
- "test/test_state_machine-mongoid.rb"
+ "spec/spec_helper.rb",
+ "spec/state_machine-mongoid_spec.rb",
+ "spec/vehicle.rb"
]
if s.respond_to? :specification_version then
@@ -42,21 +48,24 @@ Gem::Specification.new do |s|
s.specification_version = 3
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
+ s.add_runtime_dependency(%q<bson_ext>, ["= 1.0.1"])
s.add_runtime_dependency(%q<mongoid>, [">= 2.0.0.beta7"])
s.add_runtime_dependency(%q<state_machine>, [">= 0.9.2"])
s.add_runtime_dependency(%q<activemodel>, [">= 3.0.0.beta4"])
- s.add_development_dependency(%q<test-unit>, [">= 0"])
+ s.add_development_dependency(%q<rspec>, [">= 0"])
else
+ s.add_dependency(%q<bson_ext>, ["= 1.0.1"])
s.add_dependency(%q<mongoid>, [">= 2.0.0.beta7"])
s.add_dependency(%q<state_machine>, [">= 0.9.2"])
s.add_dependency(%q<activemodel>, [">= 3.0.0.beta4"])
- s.add_dependency(%q<test-unit>, [">= 0"])
+ s.add_dependency(%q<rspec>, [">= 0"])
end
else
+ s.add_dependency(%q<bson_ext>, ["= 1.0.1"])
s.add_dependency(%q<mongoid>, [">= 2.0.0.beta7"])
s.add_dependency(%q<state_machine>, [">= 0.9.2"])
s.add_dependency(%q<activemodel>, [">= 3.0.0.beta4"])
- s.add_dependency(%q<test-unit>, [">= 0"])
+ s.add_dependency(%q<rspec>, [">= 0"])
end
end
View
21 test/helper.rb
@@ -1,21 +0,0 @@
-require 'rubygems'
-require 'test/unit'
-require 'bundler'
-
-begin
- Bundler.setup(:default, :development)
-rescue Bundler::BundlerError => e
- $stderr.puts e.message
- $stderr.puts "Run `bundle install` to install missing gems"
- exit e.status_code
-end
-
-require 'active_model'
-require 'state_machine'
-
-$LOAD_PATH.unshift(File.dirname(__FILE__))
-$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
-require 'state_machine-mongoid'
-
-class Test::Unit::TestCase
-end
View
1,346 test/test_state_machine-mongoid.rb
@@ -1,1346 +0,0 @@
-require 'helper'
-
-# if ENV['VERSION'] && Gem::Version.new(ENV['VERSION']) <= Gem::Version.new('0.7.0') || !Gem.available?('>=0.7.0')
-# gem 'activesupport', '~>2.3'
-# require 'active_support'
-# end
-
-# gem 'mongoid', '>=2.0.0.beta7'
-# require 'mongoid'
-
-# Establish database connection
-MongoMapper.connection = Mongo::Connection.new('127.0.0.1', 27017, {:logger => Logger.new("#{File.dirname(__FILE__)}/../../mongo_mapper.log")})
-MongoMapper.database = 'test'
-
-module MongoMapperTest
- class BaseTestCase < Test::Unit::TestCase
- def default_test
- end
-
- protected
- # Creates a new MongoMapper model (and the associated table)
- def new_model(table_name = :foo, &block)
-
- model = Class.new do
- include MongoMapper::Document
- set_collection_name(table_name)
-
- def self.name; "MongoMapperTest::#{collection_name}"; end
- def self.to_s; "MongoMapperTest::#{collection_name}"; end
-
- key :state, String
- end
- model.class_eval(&block) if block_given?
- model.collection.remove
- model
- end
- end
-
- class IntegrationTest < BaseTestCase
- def test_should_match_if_class_includes_mongo_mapper
- assert StateMachine::Integrations::MongoMapper.matches?(new_model)
- end
-
- def test_should_not_match_if_class_does_not_include_mongo_mapper
- assert !StateMachine::Integrations::MongoMapper.matches?(Class.new)
- end
-
- def test_should_have_defaults
- assert_equal e = {:action => :save}, StateMachine::Integrations::MongoMapper.defaults
- end
- end
-
- class MachineWithoutDatabaseTest < BaseTestCase
- def setup
- @model = new_model do
- # Simulate the database not being available entirely
- def self.connection
- raise Mongo::ConnectionFailure
- end
- end
- end
-
- def test_should_allow_machine_creation
- assert_nothing_raised { StateMachine::Machine.new(@model) }
- end
- end
-
- class MachineByDefaultTest < BaseTestCase
- def setup
- @model = new_model
- @machine = StateMachine::Machine.new(@model)
- end
-
- def test_should_use_save_as_action
- assert_equal :save, @machine.action
- end
-
- def test_should_not_have_any_before_callbacks
- assert_equal 0, @machine.callbacks[:before].size
- end
-
- def test_should_not_have_any_after_callbacks
- assert_equal 0, @machine.callbacks[:after].size
- end
- end
-
- class MachineWithStaticInitialStateTest < BaseTestCase
- def setup
- @model = new_model do
- attr_accessor :value
- end
- @machine = StateMachine::Machine.new(@model, :initial => :parked)
- end
-
- def test_should_set_initial_state_on_created_object
- record = @model.new
- assert_equal 'parked', record.state
- end
-
- def test_should_set_initial_state_with_nil_attributes
- record = @model.new(nil)
- assert_equal 'parked', record.state
- end
-
- def test_should_still_set_attributes
- record = @model.new(:value => 1)
- assert_equal 1, record.value
- end
-
- def test_should_not_allow_initialize_blocks
- block_args = nil
- record = @model.new do |*args|
- block_args = args
- end
-
- assert_nil block_args
- end
-
- def test_should_set_initial_state_before_setting_attributes
- @model.class_eval do
- attr_accessor :state_during_setter
-
- define_method(:value=) do |value|
- self.state_during_setter = state
- end
- end
-
- record = @model.new(:value => 1)
- assert_equal 'parked', record.state_during_setter
- end
-
- def test_should_not_set_initial_state_after_already_initialized
- record = @model.new(:value => 1)
- assert_equal 'parked', record.state
-
- record.state = 'idling'
- record.attributes = {}
- assert_equal 'idling', record.state
- end
-
- def test_should_use_stored_values_when_loading_from_database
- @machine.state :idling
-
- record = @model.find(@model.create(:state => 'idling').id)
- assert_equal 'idling', record.state
- end
-
- def test_should_use_stored_values_when_loading_from_database_with_nil_state
- @machine.state nil
-
- record = @model.find(@model.create(:state => nil).id)
- assert_nil record.state
- end
- end
-
- class MachineWithDynamicInitialStateTest < BaseTestCase
- def setup
- @model = new_model do
- attr_accessor :value
- end
- @machine = StateMachine::Machine.new(@model, :initial => lambda {|object| :parked})
- @machine.state :parked
- end
-
- def test_should_set_initial_state_on_created_object
- record = @model.new
- assert_equal 'parked', record.state
- end
-
- def test_should_still_set_attributes
- record = @model.new(:value => 1)
- assert_equal 1, record.value
- end
-
- def test_should_not_allow_initialize_blocks
- block_args = nil
- record = @model.new do |*args|
- block_args = args
- end
-
- assert_nil block_args
- end
-
- def test_should_set_initial_state_after_setting_attributes
- @model.class_eval do
- attr_accessor :state_during_setter
-
- define_method(:value=) do |value|
- self.state_during_setter = state || 'nil'
- end
- end
-
- record = @model.new(:value => 1)
- assert_equal 'nil', record.state_during_setter
- end
-
- def test_should_not_set_initial_state_after_already_initialized
- record = @model.new(:value => 1)
- assert_equal 'parked', record.state
-
- record.state = 'idling'
- record.attributes = {}
- assert_equal 'idling', record.state
- end
-
- def test_should_use_stored_values_when_loading_from_database
- @machine.state :idling
-
- record = @model.find(@model.create(:state => 'idling').id)
- assert_equal 'idling', record.state
- end
-
- def test_should_use_stored_values_when_loading_from_database_with_nil_state
- @machine.state nil
-
- record = @model.find(@model.create(:state => nil).id)
- assert_nil record.state
- end
- end
-
- class MachineWithColumnDefaultTest < BaseTestCase
- def setup
- @model = new_model do
- key :status, String, :default => 'idling'
- end
- @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
- @record = @model.new
- end
-
- def test_should_use_machine_default
- assert_equal 'parked', @record.status
- end
- end
-
- class MachineWithConflictingPredicateTest < BaseTestCase
- def setup
- @model = new_model do
- def state?(*args)
- true
- end
- end
-
- @machine = StateMachine::Machine.new(@model)
- @record = @model.new
- end
-
- def test_should_not_define_attribute_predicate
- assert @record.state?
- end
- end
-
- class MachineWithColumnStateAttributeTest < BaseTestCase
- def setup
- @model = new_model
- @machine = StateMachine::Machine.new(@model, :initial => :parked)
- @machine.other_states(:idling)
-
- @record = @model.new
- end
-
- def test_should_not_override_the_column_reader
- @record[:state] = 'parked'
- assert_equal 'parked', @record.state
- end
-
- def test_should_not_override_the_column_writer
- @record.state = 'parked'
- assert_equal 'parked', @record[:state]
- end
-
- def test_should_have_an_attribute_predicate
- assert @record.respond_to?(:state?)
- end
-
- def test_should_test_for_existence_on_predicate_without_parameters
- assert @record.state?
-
- @record.state = nil
- assert !@record.state?
- end
-
- def test_should_return_false_for_predicate_if_does_not_match_current_value
- assert !@record.state?(:idling)
- end
-
- def test_should_return_true_for_predicate_if_matches_current_value
- assert @record.state?(:parked)
- end
-
- def test_should_raise_exception_for_predicate_if_invalid_state_specified
- assert_raise(IndexError) { @record.state?(:invalid) }
- end
- end
-
- class MachineWithNonColumnStateAttributeUndefinedTest < BaseTestCase
- def setup
- @model = new_model do
- def initialize
- # Skip attribute initialization
- @initialized_state_machines = true
- super
- end
- end
-
- @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
- @machine.other_states(:idling)
- @record = @model.new
- end
-
- def test_should_define_a_new_key_for_the_attribute
- assert_not_nil @model.keys[:status]
- end
-
- def test_should_define_a_reader_attribute_for_the_attribute
- assert @record.respond_to?(:status)
- end
-
- def test_should_define_a_writer_attribute_for_the_attribute
- assert @record.respond_to?(:status=)
- end
-
- def test_should_define_an_attribute_predicate
- assert @record.respond_to?(:status?)
- end
- end
-
- class MachineWithNonColumnStateAttributeDefinedTest < BaseTestCase
- def setup
- @model = new_model do
- attr_accessor :status
- end
-
- @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
- @machine.other_states(:idling)
- @record = @model.new
- end
-
- def test_should_return_false_for_predicate_if_does_not_match_current_value
- assert !@record.status?(:idling)
- end
-
- def test_should_return_true_for_predicate_if_matches_current_value
- assert @record.status?(:parked)
- end
-
- def test_should_raise_exception_for_predicate_if_invalid_state_specified
- assert_raise(IndexError) { @record.status?(:invalid) }
- end
-
- def test_should_set_initial_state_on_created_object
- assert_equal 'parked', @record.status
- end
- end
-
- class MachineWithAliasedAttributeTest < BaseTestCase
- def setup
- @model = new_model do
- alias_attribute :vehicle_status, :state
- end
-
- @machine = StateMachine::Machine.new(@model, :status, :attribute => :vehicle_status)
- @machine.state :parked
-
- @record = @model.new
- end
-
- def test_should_check_custom_attribute_for_predicate
- @record.vehicle_status = nil
- assert !@record.status?(:parked)
-
- @record.vehicle_status = 'parked'
- assert @record.status?(:parked)
- end
- end
-
- class MachineWithInitializedStateTest < BaseTestCase
- def setup
- @model = new_model
- @machine = StateMachine::Machine.new(@model, :initial => :parked)
- @machine.state nil, :idling
- end
-
- def test_should_allow_nil_initial_state_when_static
- record = @model.new(:state => nil)
- assert_nil record.state
- end
-
- def test_should_allow_nil_initial_state_when_dynamic
- @machine.initial_state = lambda {:parked}
- record = @model.new(:state => nil)
- assert_nil record.state
- end
-
- def test_should_allow_different_initial_state_when_static
- record = @model.new(:state => 'idling')
- assert_equal 'idling', record.state
- end
-
- def test_should_allow_different_initial_state_when_dynamic
- @machine.initial_state = lambda {:parked}
- record = @model.new(:state => 'idling')
- assert_equal 'idling', record.state
- end
-
- if defined?(MongoMapper::Plugins::Protected)
- def test_should_use_default_state_if_protected
- @model.class_eval do
- attr_protected :state
- end
-
- record = @model.new(:state => 'idling')
- assert_equal 'parked', record.state
- end
- end
- end
-
- class MachineWithLoopbackTest < BaseTestCase
- def setup
- @model = new_model do
- key :updated_at, Time
-
- before_update do |record|
- record.updated_at = Time.now
- end
- end
-
- @machine = StateMachine::Machine.new(@model, :initial => :parked)
- @machine.event :park
-
- @record = @model.create(:updated_at => Time.now - 1)
- @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
-
- @timestamp = @record.updated_at
- @transition.perform
- end
-
- def test_should_update_record
- assert_not_equal @timestamp, @record.updated_at
- end
- end
-
- class MachineWithDirtyAttributesTest < BaseTestCase
- def setup
- @model = new_model
- @machine = StateMachine::Machine.new(@model, :initial => :parked)
- @machine.event :ignite
- @machine.state :idling
-
- @record = @model.create
-
- @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
- @transition.perform(false)
- end
-
- def test_should_include_state_in_changed_attributes
- assert_equal %w(state), @record.changed
- end
-
- def test_should_track_attribute_change
- assert_equal %w(parked idling), @record.changes['state']
- end
-
- def test_should_not_reset_changes_on_multiple_transitions
- transition = StateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
- transition.perform(false)
-
- assert_equal %w(parked idling), @record.changes['state']
- end
-
- def test_should_not_have_changes_when_loaded_from_database
- record = @model.find(@record.id)
- assert !record.changed?
- end
- end
-
- class MachineWithDirtyAttributesDuringLoopbackTest < BaseTestCase
- def setup
- @model = new_model
- @machine = StateMachine::Machine.new(@model, :initial => :parked)
- @machine.event :park
-
- @record = @model.create
-
- @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
- @transition.perform(false)
- end
-
- def test_should_include_state_in_changed_attributes
- assert_equal %w(state), @record.changed
- end
-
- def test_should_track_attribute_changes
- assert_equal %w(parked parked), @record.changes['state']
- end
- end
-
- class MachineWithDirtyAttributesAndCustomAttributeTest < BaseTestCase
- def setup
- @model = new_model do
- key :status, String, :default => 'idling'
- end
- @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
- @machine.event :ignite
- @machine.state :idling
-
- @record = @model.create
-
- @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
- @transition.perform(false)
- end
-
- def test_should_include_state_in_changed_attributes
- assert_equal %w(status), @record.changed
- end
-
- def test_should_track_attribute_change
- assert_equal %w(parked idling), @record.changes['status']
- end
-
- def test_should_not_reset_changes_on_multiple_transitions
- transition = StateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
- transition.perform(false)
-
- assert_equal %w(parked idling), @record.changes['status']
- end
- end
-
- class MachineWithDirtyAttributeAndCustomAttributesDuringLoopbackTest < BaseTestCase
- def setup
- @model = new_model do
- key :status, String, :default => 'idling'
- end
- @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
- @machine.event :park
-
- @record = @model.create
-
- @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
- @transition.perform(false)
- end
-
- def test_should_include_state_in_changed_attributes
- assert_equal %w(status), @record.changed
- end
-
- def test_should_track_attribute_changes
- assert_equal %w(parked parked), @record.changes['status']
- end
- end
-
- class MachineWithCallbacksTest < BaseTestCase
- def setup
- @model = new_model
- @machine = StateMachine::Machine.new(@model, :initial => :parked)
- @machine.other_states :idling
- @machine.event :ignite
-
- @record = @model.new(:state => 'parked')
- @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
- end
-
- def test_should_run_before_callbacks
- called = false
- @machine.before_transition {called = true}
-
- @transition.perform
- assert called
- end
-
- def test_should_pass_record_to_before_callbacks_with_one_argument
- record = nil
- @machine.before_transition {|arg| record = arg}
-
- @transition.perform
- assert_equal @record, record
- end
-
- def test_should_pass_record_and_transition_to_before_callbacks_with_multiple_arguments
- callback_args = nil
- @machine.before_transition {|*args| callback_args = args}
-
- @transition.perform
- assert_equal [@record, @transition], callback_args
- end
-
- def test_should_run_before_callbacks_outside_the_context_of_the_record
- context = nil
- @machine.before_transition {context = self}
-
- @transition.perform
- assert_equal self, context
- end
-
- def test_should_run_after_callbacks
- called = false
- @machine.after_transition {called = true}
-
- @transition.perform
- assert called
- end
-
- def test_should_pass_record_to_after_callbacks_with_one_argument
- record = nil
- @machine.after_transition {|arg| record = arg}
-
- @transition.perform
- assert_equal @record, record
- end
-
- def test_should_pass_record_and_transition_to_after_callbacks_with_multiple_arguments
- callback_args = nil
- @machine.after_transition {|*args| callback_args = args}
-
- @transition.perform
- assert_equal [@record, @transition], callback_args
- end
-
- def test_should_run_after_callbacks_outside_the_context_of_the_record
- context = nil
- @machine.after_transition {context = self}
-
- @transition.perform
- assert_equal self, context
- end
-
- def test_should_run_around_callbacks
- before_called = false
- after_called = false
- @machine.around_transition {|block| before_called = true; block.call; after_called = true}
-
- @transition.perform
- assert before_called
- assert after_called
- end
-
- def test_should_include_transition_states_in_known_states
- @machine.before_transition :to => :first_gear, :do => lambda {}
-
- assert_equal [:parked, :idling, :first_gear], @machine.states.map {|state| state.name}
- end
-
- def test_should_allow_symbolic_callbacks
- callback_args = nil
-
- klass = class << @record; self; end
- klass.send(:define_method, :after_ignite) do |*args|
- callback_args = args
- end
-
- @machine.before_transition(:after_ignite)
-
- @transition.perform
- assert_equal [@transition], callback_args
- end
-
- def test_should_allow_string_callbacks
- class << @record
- attr_reader :callback_result
- end
-
- @machine.before_transition('@callback_result = [1, 2, 3]')
- @transition.perform
-
- assert_equal [1, 2, 3], @record.callback_result
- end
- end
-
- class MachineWithFailedBeforeCallbacksTest < BaseTestCase
- def setup
- @callbacks = []
-
- @model = new_model
- @machine = StateMachine::Machine.new(@model)
- @machine.state :parked, :idling
- @machine.event :ignite
- @machine.before_transition {@callbacks << :before_1; false}
- @machine.before_transition {@callbacks << :before_2}
- @machine.after_transition {@callbacks << :after}
- @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
-
- @record = @model.new(:state => 'parked')
- @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
- @result = @transition.perform
- end
-
- def test_should_be_successful
- assert @result
- end
-
- def test_should_change_current_state
- assert_equal 'idling', @record.state
- end
-
- def test_should_run_action
- assert !@record.new_record?
- end
-
- def test_should_run_further_callbacks
- assert_equal [:before_1, :before_2, :around_before, :around_after, :after], @callbacks
- end
- end
-
- class MachineWithFailedActionTest < BaseTestCase
- def setup
- @model = new_model do
- validates_inclusion_of :state, :within => %w(first_gear)
- end
-
- @machine = StateMachine::Machine.new(@model)
- @machine.state :parked, :idling
- @machine.event :ignite
-
- @callbacks = []
- @machine.before_transition {@callbacks << :before}
- @machine.after_transition {@callbacks << :after}
- @machine.after_transition(:include_failures => true) {@callbacks << :after_failure}
- @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
- @machine.around_transition(:include_failures => true) do |block|
- @callbacks << :around_before_failure
- block.call
- @callbacks << :around_after_failure
- end
-
- @record = @model.new(:state => 'parked')
- @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
- @result = @transition.perform
- end
-
- def test_should_not_be_successful
- assert !@result
- end
-
- def test_should_not_change_current_state
- assert_equal 'parked', @record.state
- end
-
- def test_should_not_save_record
- assert @record.new_record?
- end
-
- def test_should_run_before_callbacks_and_after_callbacks_with_failures
- assert_equal [:before, :around_before, :around_before_failure, :around_after_failure, :after_failure], @callbacks
- end
- end
-
- class MachineWithFailedAfterCallbacksTest < BaseTestCase
- def setup
- @callbacks = []
-
- @model = new_model
- @machine = StateMachine::Machine.new(@model)
- @machine.state :parked, :idling
- @machine.event :ignite
- @machine.after_transition {@callbacks << :after_1; false}
- @machine.after_transition {@callbacks << :after_2}
- @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
-
- @record = @model.new(:state => 'parked')
- @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
- @result = @transition.perform
- end
-
- def test_should_be_successful
- assert @result
- end
-
- def test_should_change_current_state
- assert_equal 'idling', @record.state
- end
-
- def test_should_save_record
- assert !@record.new_record?
- end
-
- def test_should_still_run_further_after_callbacks
- assert_equal [:around_before, :around_after, :after_1, :after_2], @callbacks
- end
- end
-
- class MachineWithValidationsTest < BaseTestCase
- def setup
- @model = new_model
- @machine = StateMachine::Machine.new(@model)
- @machine.state :parked
-
- @record = @model.new
- end
-
- def test_should_invalidate_using_errors
- @record.state = 'parked'
-
- @machine.invalidate(@record, :state, :invalid_transition, [[:event, :park]])
- assert_equal ['State cannot transition via "park"'], @record.errors.full_messages
- end
-
- def test_should_auto_prefix_custom_attributes_on_invalidation
- @machine.invalidate(@record, :event, :invalid)
-
- assert_equal ['State event is invalid'], @record.errors.full_messages
- end
-
- def test_should_clear_errors_on_reset
- @record.state = 'parked'
- @record.errors.add(:state, 'is invalid')
-
- @machine.reset(@record)
- assert_equal [], @record.errors.full_messages
- end
-
- def test_should_be_valid_if_state_is_known
- @record.state = 'parked'
-
- assert @record.valid?
- end
-
- def test_should_not_be_valid_if_state_is_unknown
- @record.state = 'invalid'
-
- assert !@record.valid?
- assert_equal ['State is invalid'], @record.errors.full_messages
- end
- end
-
- class MachineWithValidationsAndCustomAttributeTest < BaseTestCase
- def setup
- @model = new_model do
- alias_attribute :status, :state
- end
-
- @machine = StateMachine::Machine.new(@model, :status, :attribute => :state)
- @machine.state :parked
-
- @record = @model.new
- end
-
- def test_should_add_validation_errors_to_custom_attribute
- @record.state = 'invalid'
-
- assert !@record.valid?
- assert_equal ['State is invalid'], @record.errors.full_messages
-
- @record.state = 'parked'
- assert @record.valid?
- end
- end
-
- class MachineWithStateDrivenValidationsTest < BaseTestCase
- def setup
- @model = new_model do
- attr_accessor :seatbealt
- end
-
- @machine = StateMachine::Machine.new(@model)
- @machine.state :first_gear do
- validates_presence_of :seatbelt, :key => :first_gear
- end
- @machine.state :second_gear do
- validates_presence_of :seatbelt, :key => :second_gear
- end
- @machine.other_states :parked
- end
-
- def test_should_be_valid_if_validation_fails_outside_state_scope
- record = @model.new(:state => 'parked', :seatbelt => nil)
- assert record.valid?
- end
-
- def test_should_be_invalid_if_validation_fails_within_state_scope
- record = @model.new(:state => 'first_gear', :seatbelt => nil)
- assert !record.valid?
- end
-
- def test_should_be_valid_if_validation_succeeds_within_state_scope
- record = @model.new(:state => 'second_gear', :seatbelt => true)
- assert record.valid?
- end
- end
-
- class MachineWithEventAttributesOnValidationTest < BaseTestCase
- def setup
- @model = new_model
- @machine = StateMachine::Machine.new(@model)
- @machine.event :ignite do
- transition :parked => :idling
- end
-
- @record = @model.new
- @record.state = 'parked'
- @record.state_event = 'ignite'
- end
-
- def test_should_fail_if_event_is_invalid
- @record.state_event = 'invalid'
- assert !@record.valid?
- assert_equal ['State event is invalid'], @record.errors.full_messages
- end
-
- def test_should_fail_if_event_has_no_transition
- @record.state = 'idling'
- assert !@record.valid?
- assert_equal ['State event cannot transition when idling'], @record.errors.full_messages
- end
-
- def test_should_be_successful_if_event_has_transition
- assert @record.valid?
- end
-
- def test_should_run_before_callbacks
- ran_callback = false
- @machine.before_transition { ran_callback = true }
-
- @record.valid?
- assert ran_callback
- end
-
- def test_should_run_around_callbacks_before_yield
- ran_callback = false
- @machine.around_transition {|block| ran_callback = true; block.call }
-
- @record.valid?
- assert ran_callback
- end
-
- def test_should_persist_new_state
- @record.valid?
- assert_equal 'idling', @record.state
- end
-
- def test_should_not_run_after_callbacks
- ran_callback = false
- @machine.after_transition { ran_callback = true }
-
- @record.valid?
- assert !ran_callback
- end
-
- def test_should_not_run_after_callbacks_with_failures_disabled_if_validation_fails
- @model.class_eval do
- attr_accessor :seatbelt
- validates_presence_of :seatbelt
- end
-
- ran_callback = false
- @machine.after_transition { ran_callback = true }
-
- @record.valid?
- assert !ran_callback
- end
-
- def test_should_not_run_around_callbacks_after_yield
- ran_callback = false
- @machine.around_transition {|block| block.call; ran_callback = true }
-
- @record.valid?
- assert !ran_callback
- end
-
- def test_should_not_run_around_callbacks_after_yield_with_failures_disabled_if_validation_fails
- @model.class_eval do
- attr_accessor :seatbelt
- validates_presence_of :seatbelt
- end
-
- ran_callback = false
- @machine.around_transition {|block| block.call; ran_callback = true }
-
- @record.valid?
- assert !ran_callback
- end
-
- def test_should_run_around_callbacks_after_yield_with_failures_enabled_if_validation_fails
- @model.class_eval do
- attr_accessor :seatbelt
- validates_presence_of :seatbelt
- end
-
- ran_callback = false
- @machine.around_transition(:include_failures => true) {|block| block.call; ran_callback = true }
-
- @record.valid?
- assert ran_callback
- end
-
- def test_should_run_after_callbacks_with_failures_enabled_if_validation_fails
- @model.class_eval do
- attr_accessor :seatbelt
- validates_presence_of :seatbelt
- end
-
- ran_callback = false
- @machine.after_transition(:include_failures => true) { ran_callback = true }
-
- @record.valid?
- assert ran_callback
- end
- end
-
- class MachineWithEventAttributesOnSaveTest < BaseTestCase
- def setup
- @model = new_model
- @machine = StateMachine::Machine.new(@model)
- @machine.event :ignite do
- transition :parked => :idling
- end
-
- @record = @model.new
- @record.state = 'parked'
- @record.state_event = 'ignite'
- end
-
- def test_should_fail_if_event_is_invalid
- @record.state_event = 'invalid'
- assert_equal false, @record.save
- end
-
- def test_should_fail_if_event_has_no_transition
- @record.state = 'idling'
- assert_equal false, @record.save
- end
-
- def test_should_be_successful_if_event_has_transition
- assert_equal true, @record.save
- end
-
- def test_should_run_before_callbacks
- ran_callback = false
- @machine.before_transition { ran_callback = true }
-
- @record.save
- assert ran_callback
- end
-
- def test_should_run_before_callbacks_once
- before_count = 0
- @machine.before_transition { before_count += 1 }
-
- @record.save
- assert_equal 1, before_count
- end
-
- def test_should_run_around_callbacks_before_yield
- ran_callback = false
- @machine.around_transition {|block| ran_callback = true; block.call }
-
- @record.save
- assert ran_callback
- end
-
- def test_should_run_around_callbacks_before_yield_once
- around_before_count = 0
- @machine.around_transition {|block| around_before_count += 1; block.call }
-
- @record.save
- assert_equal 1, around_before_count
- end
-
- def test_should_persist_new_state
- @record.save
- assert_equal 'idling', @record.state
- end
-
- def test_should_run_after_callbacks
- ran_callback = false
- @machine.after_transition { ran_callback = true }
-
- @record.save
- assert ran_callback
- end
-
- def test_should_not_run_after_callbacks_with_failures_disabled_if_fails
- @model.class_eval do
- validates_inclusion_of :state, :within => %w(first_gear)
- end
-
- ran_callback = false
- @machine.after_transition { ran_callback = true }
-
- begin; @record.save; rescue; end
- assert !ran_callback
- end
-
- def test_should_run_after_callbacks_with_failures_enabled_if_fails
- @model.class_eval do
- validates_inclusion_of :state, :within => %w(first_gear)
- end
-
- ran_callback = false
- @machine.after_transition(:include_failures => true) { ran_callback = true }
-
- begin; @record.save; rescue; end
- assert ran_callback
- end
-
- def test_should_not_run_around_callbacks_with_failures_disabled_if_fails
- @model.class_eval do
- validates_inclusion_of :state, :within => %w(first_gear)
- end
-
- ran_callback = false
- @machine.around_transition {|block| block.call; ran_callback = true }
-
- begin; @record.save; rescue; end
- assert !ran_callback
- end
-
- def test_should_run_around_callbacks_after_yield
- ran_callback = false
- @machine.around_transition {|block| block.call; ran_callback = true }
-
- @record.save
- assert ran_callback
- end
-
- def test_should_run_around_callbacks_after_yield_with_failures_enabled_if_fails
- @model.class_eval do
- validates_inclusion_of :state, :within => %w(first_gear)
- end
-
- ran_callback = false
- @machine.around_transition(:include_failures => true) {|block| block.call; ran_callback = true }
-
- begin; @record.save; rescue; end
- assert ran_callback
- end
- end
-
- class MachineWithEventAttributesOnSaveBangTest < BaseTestCase
- def setup
- @model = new_model
- @machine = StateMachine::Machine.new(@model)
- @machine.event :ignite do
- transition :parked => :idling
- end
-
- @record = @model.new
- @record.state = 'parked'
- @record.state_event = 'ignite'
- end
-
- def test_should_fail_if_event_is_invalid
- @record.state_event = 'invalid'
- assert_raise(MongoMapper::DocumentNotValid) { @record.save! }
- end
-
- def test_should_fail_if_event_has_no_transition
- @record.state = 'idling'
- assert_raise(MongoMapper::DocumentNotValid) { @record.save! }
- end
-
- def test_should_be_successful_if_event_has_transition
- assert_equal true, @record.save!
- end
-
- def test_should_run_before_callbacks
- ran_callback = false
- @machine.before_transition { ran_callback = true }
-
- @record.save!
- assert ran_callback
- end
-
- def test_should_run_before_callbacks_once
- before_count = 0
- @machine.before_transition { before_count += 1 }
-
- @record.save!
- assert_equal 1, before_count
- end
-
- def test_should_run_around_callbacks_before_yield
- ran_callback = false
- @machine.around_transition {|block| ran_callback = true; block.call }
-
- @record.save!
- assert ran_callback
- end
-
- def test_should_run_around_callbacks_before_yield_once
- around_before_count = 0
- @machine.around_transition {|block| around_before_count += 1; block.call }
-
- @record.save!
- assert_equal 1, around_before_count
- end
-
- def test_should_persist_new_state
- @record.save!
- assert_equal 'idling', @record.state
- end
-
- def test_should_persist_new_state
- @record.save!
- assert_equal 'idling', @record.state
- end
-
- def test_should_run_after_callbacks
- ran_callback = false
- @machine.after_transition { ran_callback = true }
-
- @record.save!
- assert ran_callback
- end
-
- def test_should_run_around_callbacks_after_yield
- ran_callback = false
- @machine.around_transition {|block| block.call; ran_callback = true }
-
- @record.save!
- assert ran_callback
- end
- end
-
- class MachineWithEventAttributesOnCustomActionTest < BaseTestCase
- def setup
- @superclass = new_model do
- def persist
- create_or_update
- end
- end
- @model = Class.new(@superclass)
- @machine = StateMachine::Machine.new(@model, :action => :persist)
- @machine.event :ignite do
- transition :parked => :idling
- end
-
- @record = @model.new
- @record.state = 'parked'
- @record.state_event = 'ignite'
- end
-
- def test_should_not_transition_on_valid?
- @record.valid?
- assert_equal 'parked', @record.state
- end
-
- def test_should_not_transition_on_save
- @record.save
- assert_equal 'parked', @record.state
- end
-
- def test_should_not_transition_on_save!
- @record.save!
- assert_equal 'parked', @record.state
- end
-
- def test_should_transition_on_custom_action
- @record.persist
- assert_equal 'idling', @record.state
- end
- end
-
- class MachineWithScopesTest < BaseTestCase
- def setup
- @model = new_model
- @machine = StateMachine::Machine.new(@model)
- @machine.state :parked, :first_gear
- @machine.state :idling, :value => lambda {'idling'}
- end
-
- def test_should_create_singular_with_scope
- assert @model.respond_to?(:with_state)
- end
-
- def test_should_only_include_records_with_state_in_singular_with_scope
- parked = @model.create :state => 'parked'
- idling = @model.create :state => 'idling'
-
- assert_equal [parked], @model.with_state(:parked)
- end
-
- def test_should_create_plural_with_scope
- assert @model.respond_to?(:with_states)
- end
-
- def test_should_only_include_records_with_states_in_plural_with_scope
- parked = @model.create :state => 'parked'
- idling = @model.create :state => 'idling'
-
- assert_equal [parked, idling], @model.with_states(:parked, :idling)
- end
-
- def test_should_create_singular_without_scope
- assert @model.respond_to?(:without_state)
- end
-
- def test_should_only_include_records_without_state_in_singular_without_scope
- parked = @model.create :state => 'parked'
- idling = @model.create :state => 'idling'
-
- assert_equal [parked], @model.without_state(:idling)
- end
-
- def test_should_create_plural_without_scope
- assert @model.respond_to?(:without_states)
- end
-
- def test_should_only_include_records_without_states_in_plural_without_scope
- parked = @model.create :state => 'parked'
- idling = @model.create :state => 'idling'
- first_gear = @model.create :state => 'first_gear'
-
- assert_equal [parked, idling], @model.without_states(:first_gear)
- end
- end
-
- class MachineWithScopesAndOwnerSubclassTest < BaseTestCase
- def setup
- @model = new_model
- @machine = StateMachine::Machine.new(@model, :state)
-
- @subclass = Class.new(@model)
- @subclass_machine = @subclass.state_machine(:state) {}
- @subclass_machine.state :parked, :idling, :first_gear
- end
-
- def test_should_only_include_records_with_subclass_states_in_with_scope
- parked = @subclass.create :state => 'parked'
- idling = @subclass.create :state => 'idling'
-
- assert_equal [parked, idling], @subclass.with_states(:parked, :idling)
- end
-
- def test_should_only_include_records_without_subclass_states_in_without_scope
- parked = @subclass.create :state => 'parked'
- idling = @subclass.create :state => 'idling'
- first_gear = @subclass.create :state => 'first_gear'
-
- assert_equal [parked, idling], @subclass.without_states(:first_gear)
- end
- end
-
- class MachineWithComplexPluralizationScopesTest < BaseTestCase
- def setup
- @model = new_model
- @machine = StateMachine::Machine.new(@model, :status)
- end
-
- def test_should_create_singular_with_scope
- assert @model.respond_to?(:with_status)
- end
-
- def test_should_create_plural_with_scope
- assert @model.respond_to?(:with_statuses)
- end
- end
-end
Please sign in to comment.
Something went wrong with that request. Please try again.