diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 92aef51..6d2e40e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,10 +14,10 @@ jobs: strategy: fail-fast: false matrix: - ruby: [2.4, 2.5, 2.6, 2.7, 3.0, 3.1] + ruby: [3.1, 3.3] steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b695504 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,19 @@ + +# Changelog + +## New in the version 3.1.0 + +* [#227](https://github.com/geekq/workflow/pull/227) Allow event arguments to be taken into account when selecting the event +* [#232](https://github.com/geekq/workflow/pull/232) Add ability to include partial workflow definitions for composability +* [#241](https://github.com/geekq/workflow/pull/241) Example for defining workflow dynamically from JSON + +## New in the version 3.0.0 + +* [#228](https://github.com/geekq/workflow/pull/228) Support for Ruby 3 keyword args, provided by @agirling +* retire Ruby 2.6 since it has reached end of live; please use workflow 2.x, if you still depend on that Ruby version +* [#229](https://github.com/geekq/workflow/pull/229) Switch from travis CI to GihHub actions for continuous integration + +## New in the versions 2.x + +* extract persistence adapters, Rails/ActiveRecord integration is now a separate gem + workflow-activerecord diff --git a/README.adoc b/README.adoc index e8ef40f..cfd7d54 100644 --- a/README.adoc +++ b/README.adoc @@ -16,15 +16,6 @@ at http://rubygems.org/gems/workflow : select a version (optional, default is latest release), click "Documentation" link. When reading on github.com, the README refers to the upcoming release. -**Note: Workflow 2.0 is a major refactoring of the library. -For different options/troubleshooting using it with your Rails application see -link:#activerecord[State persistence with ActiveRecord].** - -Note for contributors: it looks like github closed all the pull requests after -I had changed the default branch on 2019-01-12. Please check the new refactored -workflow 2.0, complementing workflow-activerecord and recreate your pull -request if needed. - toc::[] What is workflow? @@ -111,7 +102,7 @@ article.current_state.between? :awaiting_review, :rejected # => true ``` Now we can call the submit event, which transitions to the -:awaiting_review state: +`:awaiting_review` state: ```rb article.submit! @@ -140,7 +131,7 @@ gem install workflow install the `activesupport` and `ruby-graphviz` gems. Versions up to and including 1.0.0 are also available as a single file download - -[lib/workflow.rb file](https://github.com/geekq/workflow/blob/v1.0.0/lib/workflow.rb). +link:https://github.com/geekq/workflow/blob/v1.0.0/lib/workflow.rb[lib/workflow.rb file]. === Examples @@ -354,43 +345,6 @@ favorite database. Advanced usage -------------- -### Accessing your workflow specification - -You can easily reflect on workflow specification programmatically - for -the whole class or for the current object. Examples: - -```rb -article2.current_state.events # lists possible events from here -article2.current_state.events[:reject].transitions_to # => :rejected - -Article.workflow_spec.states.keys -#=> [:rejected, :awaiting_review, :being_reviewed, :accepted, :new] - -Article.workflow_spec.state_names -#=> [:rejected, :awaiting_review, :being_reviewed, :accepted, :new] - -# list all events for all states -Article.workflow_spec.states.values.collect &:events -``` - -You can also store and later retrieve additional meta data for every -state and every event: - -```rb -class MyProcess - include Workflow - workflow do - state :main, :meta => {:importance => 8} - state :supplemental, :meta => {:importance => 1} - end -end -puts MyProcess.workflow_spec.states[:supplemental].meta[:importance] # => 1 -``` - -The workflow library itself uses this feature to tweak the graphical -representation of the workflow. See below. - - ### Conditional event transitions Conditions can be a "method name symbol" with a corresponding instance method, a `proc` or `lambda` which are added to events, like so: @@ -416,6 +370,8 @@ When calling a `device.can_?` check, or attempting a `device. * If an `:if` check is present, proceed if it evaluates to true, or drop to the next event. * If you've run out of events to check (eg. `battery_level == 0`), then the transition isn't possible. +You can also pass additional arguments, which can be evaluated by :if methods or procs. See examples in +link:test/conditionals_test.rb#L45[conditionals_test.rb] ### Advanced transition hooks @@ -496,50 +452,76 @@ The whole event sequence is as follows: * event specific action * on_transition (if action did not halt) * on_exit - * PERSIST WORKFLOW STATE, i.e. transition + * PERSIST WORKFLOW STATE (i.e. transition) or on_error * on_entry * after_transition -Documenting with diagrams -------------------------- +### Accessing your workflow specification -You can generate a graphical representation of the workflow for -a particular class for documentation purposes. -Use `Workflow::create_workflow_diagram(class)` in your rake task like: +You can easily reflect on workflow specification programmatically - for +the whole class or for the current object. Examples: ```rb -namespace :doc do - desc "Generate a workflow graph for a model passed e.g. as 'MODEL=Order'." - task :workflow => :environment do - require 'workflow/draw' - Workflow::Draw::workflow_diagram(ENV['MODEL'].constantize) +article2.current_state.events # lists possible events from here +article2.current_state.events[:reject].transitions_to # => :rejected + +Article.workflow_spec.states.keys +#=> [:rejected, :awaiting_review, :being_reviewed, :accepted, :new] + +Article.workflow_spec.state_names +#=> [:rejected, :awaiting_review, :being_reviewed, :accepted, :new] + +# list all events for all states +Article.workflow_spec.states.values.collect &:events +``` + +You can also store and later retrieve additional meta data for every +state and every event: + +```rb +class MyProcess + include Workflow + workflow do + state :main, :meta => {:importance => 8} + state :supplemental, :meta => {:importance => 1} end end +puts MyProcess.workflow_spec.states[:supplemental].meta[:importance] # => 1 ``` +The workflow library itself uses this feature to tweak the graphical +representation of the workflow. See below. -Changelog ---------- -### New in the version 2.0.2 +### Defining workflow dynamically from JSON -* finalize extraction of persistence adapters, remove remodel adapter +For an advance example please see +link:https://github.com/geekq/workflow/blob/develop/test/workflow_from_json_test.rb[workflow_from_json_test.rb]. -### New in the version 2.0.1 -* retire Ruby 2.3 since it has reached end of live -* fix #213 ruby-graphiz warnings +### Compose workflow definition with `include` -### New in the version 2.0.0 +In case you have very extensive workflow definition or would like to reuse +workflow definition for different classes, you can include parts like in +the link:https://github.com/geekq/workflow/blob/develop/test/main_test.rb#L95-L110[`including a child workflow definition` example]. -* extract Rails/ActiveRecord integration into a separate gem - workflow-activerecord -* Remodel integration removed - needs to be a separate gem +Documenting with diagrams +------------------------- -Special thanks to https://github.com/voltechs[voltechs] for implementing -Rails 5 support and helping to revive `workflow`! +You can generate a graphical representation of the workflow for +a particular class for documentation purposes. +Use `Workflow::create_workflow_diagram(class)` in your rake task like: +```rb +namespace :doc do + desc "Generate a workflow graph for a model passed e.g. as 'MODEL=Order'." + task :workflow => :environment do + require 'workflow/draw' + Workflow::Draw::workflow_diagram(ENV['MODEL'].constantize) + end +end +``` Support, Participation ---------------------- @@ -560,6 +542,11 @@ bundle install bundle exec rake test ``` +### Check list for you pull request + +* [ ] unit tests for the new behavior provided: new tests fail without you change, all tests succeed with your change +* [ ] documentation update included + ### Other 3rd party libraries https://github.com/kwent/active_admin-workflow[ActiveAdmin-Workflow] - is an @@ -569,7 +556,7 @@ integration with https://github.com/activeadmin/activeadmin[ActiveAdmin]. Author: Vladimir Dobriakov, -Copyright (c) 2010-2019 Vladimir Dobriakov and Contributors +Copyright (c) 2010-2024 Vladimir Dobriakov and Contributors Copyright (c) 2008-2009 Vodafone @@ -578,4 +565,3 @@ Copyright (c) 2007-2008 Ryan Allen, FlashDen Pty Ltd Based on the work of Ryan Allen and Scott Barron Licensed under MIT license, see the MIT-LICENSE file. - diff --git a/lib/workflow.rb b/lib/workflow.rb index 558e281..31e639d 100644 --- a/lib/workflow.rb +++ b/lib/workflow.rb @@ -2,7 +2,7 @@ require 'workflow/specification' -# See also README.markdown for documentation +# See also README for documentation module Workflow module ClassMethods attr_reader :workflow_spec @@ -61,12 +61,12 @@ def assign_workflow(specification_object) state.events.flat.each do |event| event_name = event.name module_eval do - define_method "#{event_name}!".to_sym do |*args| - process_event!(event_name, *args) + define_method "#{event_name}!".to_sym do |*args, **kwargs| + process_event!(event_name, *args, **kwargs) end - define_method "can_#{event_name}?" do - return !!current_state.events.first_applicable(event_name, self) + define_method "can_#{event_name}?".to_sym do |*args, **kwargs| + return !!current_state.events.first_applicable(event_name, self, args) end end end @@ -94,8 +94,8 @@ def halted_because @halted_because end - def process_event!(name, *args) - event = current_state.events.first_applicable(name, self) + def process_event!(name, *args, **kwargs) + event = current_state.events.first_applicable(name, self, args) raise NoTransitionAllowed.new( "There is no event #{name.to_sym} defined for the #{current_state} state") \ if event.nil? @@ -107,26 +107,26 @@ def process_event!(name, *args) from = current_state to = spec.states[event.transitions_to] - run_before_transition(from, to, name, *args) + run_before_transition(from, to, name, *args, **kwargs) return false if @halted begin - return_value = run_action(event.action, *args) || run_action_callback(event.name, *args) + return_value = run_action(event.action, *args, **kwargs) || run_action_callback(event.name, *args, **kwargs) rescue StandardError => e - run_on_error(e, from, to, name, *args) + run_on_error(e, from, to, name, *args, **kwargs) end return false if @halted - run_on_transition(from, to, name, *args) + run_on_transition(from, to, name, *args, **kwargs) - run_on_exit(from, to, name, *args) + run_on_exit(from, to, name, *args, **kwargs) transition_value = persist_workflow_state to.to_s - run_on_entry(to, from, name, *args) + run_on_entry(to, from, name, *args, **kwargs) - run_after_transition(from, to, name, *args) + run_after_transition(from, to, name, *args, **kwargs) return_value.nil? ? transition_value : return_value end @@ -169,31 +169,31 @@ def check_transition(event) end end - def run_before_transition(from, to, event, *args) - instance_exec(from.name, to.name, event, *args, &spec.before_transition_proc) if + def run_before_transition(from, to, event, *args, **kwargs) + instance_exec(from.name, to.name, event, *args, **kwargs, &spec.before_transition_proc) if spec.before_transition_proc end - def run_on_error(error, from, to, event, *args) + def run_on_error(error, from, to, event, *args, **kwargs) if spec.on_error_proc - instance_exec(error, from.name, to.name, event, *args, &spec.on_error_proc) + instance_exec(error, from.name, to.name, event, *args, **kwargs, &spec.on_error_proc) halt(error.message) else raise error end end - def run_on_transition(from, to, event, *args) - instance_exec(from.name, to.name, event, *args, &spec.on_transition_proc) if spec.on_transition_proc + def run_on_transition(from, to, event, *args, **kwargs) + instance_exec(from.name, to.name, event, *args, **kwargs, &spec.on_transition_proc) if spec.on_transition_proc end - def run_after_transition(from, to, event, *args) - instance_exec(from.name, to.name, event, *args, &spec.after_transition_proc) if + def run_after_transition(from, to, event, *args, **kwargs) + instance_exec(from.name, to.name, event, *args, **kwargs, &spec.after_transition_proc) if spec.after_transition_proc end - def run_action(action, *args) - instance_exec(*args, &action) if action + def run_action(action, *args, **kwargs) + instance_exec(*args, **kwargs, &action) if action end def has_callback?(action) @@ -206,27 +206,27 @@ def has_callback?(action) self.private_methods(false).map(&:to_sym).include?(action) end - def run_action_callback(action_name, *args) + def run_action_callback(action_name, *args, **kwargs) action = action_name.to_sym - self.send(action, *args) if has_callback?(action) + self.send(action, *args, **kwargs) if has_callback?(action) end - def run_on_entry(state, prior_state, triggering_event, *args) + def run_on_entry(state, prior_state, triggering_event, *args, **kwargs) if state.on_entry - instance_exec(prior_state.name, triggering_event, *args, &state.on_entry) + instance_exec(prior_state.name, triggering_event, *args, **kwargs, &state.on_entry) else hook_name = "on_#{state}_entry" - self.send hook_name, prior_state, triggering_event, *args if has_callback?(hook_name) + self.send hook_name, prior_state, triggering_event, *args, **kwargs if has_callback?(hook_name) end end - def run_on_exit(state, new_state, triggering_event, *args) + def run_on_exit(state, new_state, triggering_event, *args, **kwargs) if state if state.on_exit - instance_exec(new_state.name, triggering_event, *args, &state.on_exit) + instance_exec(new_state.name, triggering_event, *args, **kwargs, &state.on_exit) else hook_name = "on_#{state}_exit" - self.send hook_name, new_state, triggering_event, *args if has_callback?(hook_name) + self.send hook_name, new_state, triggering_event, *args, **kwargs if has_callback?(hook_name) end end end diff --git a/lib/workflow/event.rb b/lib/workflow/event.rb index ed5048a..c425dd7 100644 --- a/lib/workflow/event.rb +++ b/lib/workflow/event.rb @@ -15,12 +15,26 @@ def initialize(name, transitions_to, condition = nil, meta = {}, &action) end end - def condition_applicable?(object) + def condition_applicable?(object, event_arguments) if condition if condition.is_a?(Symbol) - object.send(condition) + m = object.method(condition) + # Conditionals can now take the arguments of the trasition action into account #227 + # But in case the current conditional wants to ignore any event_argument on its decision - + # does not accept parameters, also support that. + if m.arity == 0 # no additional parameters accepted + object.send(condition) + else + object.send(condition, *event_arguments) + end else - condition.call(object) + # Blocks and non-lambda Procs can ignore extra arguments without raising an error in Ruby, + # but lambdas cannot, so we still have to check arity + if condition.arity == 1 # no additional parameters accepted + condition.call(object) + else + condition.call(object, *event_arguments) + end end else true diff --git a/lib/workflow/event_collection.rb b/lib/workflow/event_collection.rb index 5a20cfc..4860bf6 100644 --- a/lib/workflow/event_collection.rb +++ b/lib/workflow/event_collection.rb @@ -26,9 +26,9 @@ def include?(name_or_obj) end end - def first_applicable(name, object_context) + def first_applicable(name, object_context, event_arguments) (self[name] || []).detect do |event| - event.condition_applicable?(object_context) && event + event.condition_applicable?(object_context, event_arguments) && event end end diff --git a/lib/workflow/specification.rb b/lib/workflow/specification.rb index b98033f..acf3102 100644 --- a/lib/workflow/specification.rb +++ b/lib/workflow/specification.rb @@ -20,6 +20,10 @@ def state_names private + def include(proc) + instance_eval(&proc) + end + def state(name, meta = {:meta => {}}, &events_and_etc) # meta[:meta] to keep the API consistent..., gah new_state = Workflow::State.new(name, self, meta[:meta]) diff --git a/lib/workflow/version.rb b/lib/workflow/version.rb index 6468839..43b9fe9 100644 --- a/lib/workflow/version.rb +++ b/lib/workflow/version.rb @@ -1,3 +1,3 @@ module Workflow - VERSION = "2.0.2" + VERSION = "3.1.1" end diff --git a/test/conditionals_test.rb b/test/conditionals_test.rb new file mode 100644 index 0000000..6f9fae3 --- /dev/null +++ b/test/conditionals_test.rb @@ -0,0 +1,116 @@ +require File.join(File.dirname(__FILE__), 'test_helper') + +$VERBOSE = false +require 'workflow' +require 'mocha/minitest' + +class ConditionalsTest < Minitest::Test + + test 'can_? with conditions' do + c = Class.new do + include Workflow + workflow do + state :off do + event :turn_on, :transitions_to => :on, :if => :sufficient_battery_level? + event :turn_on, :transitions_to => :low_battery, :if => proc { |obj| obj.battery > 0 } + end + state :on + state :low_battery + end + attr_reader :battery + def initialize(battery) + @battery = battery + end + + def sufficient_battery_level? + @battery > 10 + end + end + + device = c.new 0 + assert_equal false, device.can_turn_on? + + device = c.new 5 + assert device.can_turn_on? + device.turn_on! + assert device.low_battery? + assert_equal false, device.on? + + device = c.new 50 + assert device.can_turn_on? + device.turn_on! + assert device.on? + end + + test 'gh-227 allow event arguments in conditions - test with a method' do + c = Class.new do + include Workflow + # define more advanced workflow, where event methods allow arguments + workflow do + state :off do + # turn_on and transition filters accepts additional argument `power_adapter` + event :turn_on, :transitions_to => :on, :if => :sufficient_battery_level? + event :turn_on, :transitions_to => :low_battery # otherwise + end + state :on do + event :check, :transitions_to => :low_battery, :if => :check_low_battery? + event :check, :transitions_to => :on # stay in on state otherwise + end + state :low_battery + end + attr_reader :battery + def initialize(battery) + @battery = battery + end + + def sufficient_battery_level?(power_adapter) + power_adapter || @battery > 10 + end + + def check_low_battery?() # supports no arguments, lets test below, what happens if the action uses addtional args + # 'in check_low_battery? method' + end + end + + # test for conditions in a proc + device = c.new 5 + device.turn_on!(true) # case with event arguments to be taken into account + assert device.on? + device.check!('foo') # the conditional in the definition above does not support arguments, but make it work + # by ignoring superfluous arguments for compatibility + assert device.on? + end + + test 'gh-227 allow event arguments in conditions - test with a proc' do + c = Class.new do + include Workflow + # define more advanced workflow, where event methods allow arguments + workflow do + state :off do + # turn_on and transition filters accepts additional argument `power_adapter` + event :turn_on, :transitions_to => :on, :if => proc { |obj, power_adapter| power_adapter || obj.battery > 10 } + event :turn_on, :transitions_to => :low_battery # otherwise + end + state :on do + # Use a lambda proc, which enforces correct arity + event :check, :transitions_to => :low_battery, :if => -> (obj) { return false } + event :check, :transitions_to => :on # stay in on state otherwise + end + state :low_battery + end + attr_reader :battery + def initialize(battery) + @battery = battery + end + end + + device = c.new 5 + device.turn_on!(true) # case with event arguments to be taken into account + assert device.on? + device.check!('foo') # also ensure that if conditional in the definition above does not support arguments, + # it still works and just ignores superfluous arguments + assert device.on? + end + +end + diff --git a/test/main_test.rb b/test/main_test.rb index 6bc0f17..d7031fb 100644 --- a/test/main_test.rb +++ b/test/main_test.rb @@ -92,6 +92,23 @@ class MainTest < Minitest::Test assert_equal 'one', c.new.current_state.to_s end + test 'including a child workflow definition for composable workflows' do + child = Proc.new do + state :two + end + + c = Class.new + c.class_eval do + include Workflow + workflow do + state :one + include child + state :three + end + end + assert_equal [:one, :two, :three], c.workflow_spec.states.keys + end + # TODO Consider following test case: # test 'multiple events with the same name and different arguments lists from different states' @@ -328,42 +345,6 @@ def reject(reason) assert_equal false, human.can_go_to_college? end - test 'can_? with conditions' do - c = Class.new do - include Workflow - workflow do - state :off do - event :turn_on, :transitions_to => :on, :if => :sufficient_battery_level? - event :turn_on, :transitions_to => :low_battery, :if => proc { |obj| obj.battery > 0 } - end - state :on - state :low_battery - end - attr_reader :battery - def initialize(battery) - @battery = battery - end - - def sufficient_battery_level? - @battery > 10 - end - end - - device = c.new 0 - assert_equal false, device.can_turn_on? - - device = c.new 5 - assert device.can_turn_on? - device.turn_on! - assert device.low_battery? - assert_equal false, device.on? - - device = c.new 50 - assert device.can_turn_on? - device.turn_on! - assert device.on? - end - test 'workflow graph generation' do require 'workflow/draw' Dir.chdir('/tmp') do @@ -390,4 +371,3 @@ def capture_streams end end - diff --git a/test/workflow_from_json_test.rb b/test/workflow_from_json_test.rb new file mode 100644 index 0000000..07b443b --- /dev/null +++ b/test/workflow_from_json_test.rb @@ -0,0 +1,158 @@ +require File.join(File.dirname(__FILE__), 'test_helper') +require 'workflow' +class WorkflowFromJsonTest < Minitest::Test + + test '#241 define a workflow from JSON' do + c = Class.new + c.class_eval do + include Workflow + + attr_reader :events + + def record(event) + @events ||= [] + @events << event + end + + def ran?(event) + @events.include? event + end + + def clear_events + @events = [] + end + + def event_1(test) + # puts "running event_1 with arg '#{test}'" + record __method__ + end + + def before_transition(from, to, triggering_event, *args, **kwargs) + # puts "before_transition #{from} -> #{to}, #{triggering_event}, #{args}, #{kwargs}" + record __method__ + end + + def on_transition(from, to, triggering_event, *args, **kwargs) + # puts "on_transition #{from} -> #{to}, #{triggering_event}, #{args}, #{kwargs}" + record __method__ + end + + def after_transition(from, to, triggering_event, *args, **kwargs) + # puts "after_transition #{from} -> #{to}, #{triggering_event}, #{args}, #{kwargs}" + record __method__ + end + + def event_1_condition? + # puts "event_1_condition? -> true" + record __method__ + true + end + + def on_state_1_exit(new_state, event, *args) + # puts "on_state_1_exit(#{new_state}, #{event}, #{args})" + record __method__ + end + + def entering_state_2(prior_state, triggering_event, *args, **kwargs) + # puts "entering_state_2 #{prior_state}, #{triggering_event}, #{args}, #{kwargs}" + record __method__ + end + + def entering_state_3(prior_state, triggering_event, *args, **kwargs) + # puts "entering_state_3 #{prior_state}, #{triggering_event}, #{args}, #{kwargs}" + record __method__ + end + + def on_state_2_entry(new_state, event, *args) + # puts "on_state_2_entry(#{new_state}, #{event}, #{args})" + record __method__ + end + + end + + ran_normal = [] + hash = { + state_1: { + events: { + event_1: { + transition_to: :state_2, + if: :event_1_condition?, + meta: { e1: 1 } + }, + }, + meta: { a: 1 } + }, + state_2: { + events: { + event_2: { + transition_to: :state_3, + } + }, + on_entry: :entering_state_2, + meta: { a: 2 } + }, + state_3: { + on_entry: "entering_state_3", + } + } + + spec = Workflow::Specification.new do + + hash.each_pair do |state_name, state_def| + + state state_name, state_def + + on_entry {|prior_state, triggering_event| send state_def[:on_entry], prior_state, triggering_event } if state_def.include?(:on_entry) + on_exit { ran_normal << :on_exit } + + state_def[:events]&.each_pair do |event_name, event_def| + event event_name, event_def + end + + end + + before_transition do |from, to, triggering_event, *args, **kwargs| + ran_normal << :before_transition + before_transition(from, to, triggering_event, *args, **kwargs) + end + + on_transition do |from, to, triggering_event, *args, **kwargs| + ran_normal << :on_transition + on_transition(from, to, triggering_event, *args, **kwargs) + end + + after_transition do |from, to, triggering_event, *args, **kwargs| + ran_normal << :after_transition + after_transition(from, to, triggering_event, *args, **kwargs) + end + + end + + c.send :assign_workflow, spec + + o = c.new + + assert o.state_1?, "Should be state_1" + refute o.state_2?, "Should not be state_2" + + o.event_1! "hello" + [:event_1_condition?, :event_1, :entering_state_2, :before_transition, :on_transition, :after_transition].each do |event| + assert o.ran?(event), "Should have run event #{event}" + end + + refute o.state_1?, "Should not be state_1" + assert o.state_2?, "Should be state_2" + + o.clear_events + o.event_2! + refute o.ran?(:event_1), "Should not have run event_1" + [:entering_state_3, :before_transition, :on_transition, :after_transition].each do |event| + assert o.ran?(event), "Should have run event #{event}" + end + + assert ran_normal.include?(:before_transition), "Should have run before_transition proc" + assert ran_normal.include?(:on_transition), "Should have run on_transition proc" + assert ran_normal.include?(:after_transition), "Should have run after_transition proc" + assert ran_normal.include?(:on_exit), "Should have run on_exit proc" + end +end diff --git a/workflow.gemspec b/workflow.gemspec index 41e6e99..747f726 100644 --- a/workflow.gemspec +++ b/workflow.gemspec @@ -18,21 +18,25 @@ Gem::Specification.new do |gem| gem.licenses = ['MIT'] gem.homepage = "https://github.com/geekq/workflow" + gem.metadata["homepage_uri"] = gem.homepage + gem.metadata["source_code_uri"] = gem.homepage + gem.metadata["changelog_uri"] = "https://github.com/geekq/workflow/blob/develop/CHANGELOG.md" + gem.files = Dir['CHANGELOG.md', 'README.md', 'LICENSE', 'lib/**/*'] gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) } gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ['lib'] gem.extra_rdoc_files = [ - "README.markdown" + "README.adoc" ] - gem.required_ruby_version = '>= 2.3' - gem.add_development_dependency 'rdoc', '~> 6.1' - gem.add_development_dependency 'bundler', '~> 2.0' - gem.add_development_dependency 'mocha', '~> 1.8' - gem.add_development_dependency 'rake', '~> 12.3' - gem.add_development_dependency 'minitest', '~> 5.11' + gem.required_ruby_version = '>= 2.7' + gem.add_development_dependency 'rdoc', '~> 6.4' + gem.add_development_dependency 'bundler', '~> 2.3' + gem.add_development_dependency 'mocha', '~> 2.2' + gem.add_development_dependency 'rake', '~> 13.1' + gem.add_development_dependency 'minitest', '~> 5.21' gem.add_development_dependency 'ruby-graphviz', '~> 1.2' end