Skip to content

Commit

Permalink
Minor refactor
Browse files Browse the repository at this point in the history
Changed how services and operations are represented

Also installed Solargraph/Rubocop/StandardRB for use in nvim
And added YardDoc to most classes to help Solargraph with completions
  • Loading branch information
rahoulb committed Jun 11, 2024
1 parent f98ffc3 commit 4423154
Show file tree
Hide file tree
Showing 12 changed files with 71 additions and 50 deletions.
24 changes: 24 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# We want Exclude directives from different
# config files to get merged, not overwritten
inherit_mode:
merge:
- Exclude

require:
# Standard's config uses custom cops,
# so it must be loaded along with custom Standard gems
- standard
- standard-custom
- standard-performance
# rubocop-performance is required when using Performance cops
- rubocop-performance

inherit_gem:
standard: config/base.yml
standard-performance: config/base.yml
standard-custom: config/base.yml

# Global options, like Ruby version
AllCops:
SuggestExtensions: false
TargetRubyVersion: 3.2
1 change: 0 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ gem "rake", "~> 13.0"
gem "rspec", "~> 3.0"
gem "standard", "~> 1.3"
gem "guard"
gem "guard-standardrb"
gem "guard-rspec"
gem "guard-bundler"
gem "yard"
Expand Down
29 changes: 7 additions & 22 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ PATH
specs:
workflows (0.1.0)
dry-configurable
dry-events
dry-struct
faker
standard-procedure-plumbing

GEM
remote: https://rubygems.org/
Expand All @@ -22,9 +22,6 @@ GEM
dry-core (1.0.1)
concurrent-ruby (~> 1.0)
zeitwerk (~> 2.6)
dry-events (1.0.1)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
dry-inflector (1.0.0)
dry-logic (1.5.0)
concurrent-ruby (~> 1.0)
Expand All @@ -44,17 +41,7 @@ GEM
zeitwerk (~> 2.6)
faker (3.4.1)
i18n (>= 1.8.11, < 2)
ffi (1.17.0)
ffi (1.17.0-aarch64-linux-gnu)
ffi (1.17.0-aarch64-linux-musl)
ffi (1.17.0-arm-linux-gnu)
ffi (1.17.0-arm-linux-musl)
ffi (1.17.0-arm64-darwin)
ffi (1.17.0-x86-linux-gnu)
ffi (1.17.0-x86-linux-musl)
ffi (1.17.0-x86_64-darwin)
ffi (1.17.0-x86_64-linux-gnu)
ffi (1.17.0-x86_64-linux-musl)
ffi (1.16.3)
formatador (1.1.0)
guard (2.18.1)
formatador (>= 0.2.4)
Expand All @@ -74,10 +61,6 @@ GEM
guard (~> 2.1)
guard-compat (~> 1.1)
rspec (>= 2.99.0, < 4.0)
guard-standardrb (0.2.3)
guard (>= 2.0.0)
guard-compat (~> 1.0)
standard
i18n (1.14.5)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
Expand All @@ -88,7 +71,7 @@ GEM
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
lumberjack (1.2.10)
method_source (1.1.0)
method_source (1.0.0)
nenv (0.3.0)
notiffany (0.1.3)
nenv (~> 0.1)
Expand All @@ -104,7 +87,7 @@ GEM
rainbow (3.1.1)
rake (13.2.1)
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
rb-inotify (0.10.1)
ffi (~> 1.0)
regexp_parser (2.9.2)
rexml (3.2.8)
Expand Down Expand Up @@ -158,6 +141,9 @@ GEM
standard-performance (1.4.0)
lint_roller (~> 1.1)
rubocop-performance (~> 1.21.0)
standard-procedure-plumbing (0.1.0)
dry-struct
dry-types
strscan (3.1.0)
thor (1.3.1)
unicode-display_width (2.5.0)
Expand All @@ -181,7 +167,6 @@ DEPENDENCIES
guard
guard-bundler
guard-rspec
guard-standardrb
rake (~> 13.0)
rspec (~> 3.0)
simplecov
Expand Down
11 changes: 0 additions & 11 deletions Guardfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
ignore(/bin/, /log/, /public/, /storage/, /tmp/)

group :formatting do
guard :standardrb, fix: true, all_on_start: true, progress: true do
watch(/.+\.rb$/)
watch(/.+\.thor$/)
watch(/.+\.rake$/)
watch(/Guardfile$/)
watch(/Rakefile$/)
watch(/Gemfile$/)
end
end

group :development do
guard :rspec, cmd: "bundle exec rspec" do
watch("spec/.+_helper.rb") { "spec" }
Expand Down
12 changes: 8 additions & 4 deletions lib/workflows.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require "dry/types"
require "dry/configurable"
require "plumbing"

module Workflows
extend Dry::Configurable
Expand All @@ -13,13 +14,13 @@ module Types
Name = Strict::String

Card = Types.Interface(:id, :name, :state)
Storage = Types.Interface(:create, :find, :where, :update, :delete)
State = Types.Interface(:name, :perform_action)
States = Types::Array.of(Types::State)
Action = Types.Interface(:destination, :outputs)
Actions = Types::Hash.map(Types::Coercible::String, Types::Action)
Services = Types.Interface(:[], :resolve)
Messages = Types.Interface(:publish, :subscribe)
Operation = Types.Interface(:call)
Services = Types::Hash.map(Types::Coercible::String, Types::Operation)
Messages = Types.Interface(:<<, :add_observer, :remove_observer)
Workflow = Types.Interface(:states, :services, :messages)
end

Expand All @@ -32,5 +33,8 @@ class Error < StandardError; end
require_relative "workflows/workflow"
require_relative "workflows/card"

setting :cards, default: InMemoryCards, reader: true
# @return [Plumbing::Pipe] message queue for broadcasting events
setting :messages, default: Plumbing::Pipe.start, reader: true
# @return [Hash] a set of services available within this service
setting :services, default: {}, reader: true
end
3 changes: 3 additions & 0 deletions lib/workflows/action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
require "dry/struct"

module Workflows
# An action that can be performed on a {Card} to change its {State}
class Action < Dry::Struct
include NameToS
# @return [Workflows::State]
attribute :destination, Types::State
# @return [Array(String)]
attribute :outputs, Types::Array.of(Types::Coercible::String)
end
end
2 changes: 2 additions & 0 deletions lib/workflows/name_to_s.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module Workflows
# Convenience mix-in to return the included class's `name` as `to_s`
module NameToS
# @return [String]
def to_s
name
end
Expand Down
14 changes: 12 additions & 2 deletions lib/workflows/state.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,28 @@
require_relative "action"

module Workflows
# The state in a {Workflows::Workflow} state-machine
class State < Dry::Struct
include NameToS
# @return [String]
attribute :name, Types::Name
# @return [Array(Workflows::Action)]
attribute :actions, Types::Actions

# @param action_name [String] the name of the action to perform
# @param card [Workflows::Card] the card that will be acted upon
# @raise [Workflows::State::InvalidAction] if the action_name is invalid
def perform_action action_name, card:
card = Types::Card[card]
action = actions[action_name]
raise InvalidAction.new(action) if action.nil?
card = Workflows.cards.update card, state: action.destination

updater = Types::Operation[Workflows.services["workflows.cards.storage.update.state"]]
card = updater.call card, state: action.destination

messages = Types::Messages[Workflows.messages]
action.outputs.each do |output|
card.emit output
messages.notify output, card: card
end
end

Expand Down
3 changes: 3 additions & 0 deletions lib/workflows/workflow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
require_relative "state"

module Workflows
# Workflow represents the structure of a finite state machine that {Workflows::Card}s move through.
class Workflow < Dry::Struct
include NameToS

# @return [String]
attribute :name, Types::Name
# @return [Array(Workflows::State)]
attribute :states, Types::States
end
end
2 changes: 0 additions & 2 deletions spec/workflows/card_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,4 @@

@card.perform("some_action")
end

it "emits an event"
end
18 changes: 11 additions & 7 deletions spec/workflows/state_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,29 @@
RSpec.describe Workflows::State do
context "performing an action" do
it "updates the card's state and tells it to emit some outputs" do
Workflows.services["workflows.cards.storage.update.state"] = ->(card, state:) do
expect(state).to eq @dispatched
card
end
@dispatched = Workflows::State.new(name: "Dispatched", actions: {})
@dispatch = Workflows::Action.new(destination: @dispatched, outputs: ["card.dispatched"])
@initial = Workflows::State.new(name: "Iniital", actions: {dispatch: @dispatch})
@workflow = Workflows::Workflow.new(name: "Order processing", states: [@initial, @dispatched])
@pipe = Workflows.messages

@card = Workflows::Card.new(id: "123", name: "New order", state: @initial)

expect(Workflows.cards).to receive(:update).with(@card, state: @dispatched).and_return(@card)
expect(@card).to receive(:emit).with("card.dispatched")
expect(@pipe).to receive(:notify).with("card.dispatched", card: @card)

@initial.perform_action "dispatch", card: @card
end
end

it "raises an InvalidAction exception if the action is not valid" do
@initial = Workflows::State.new(name: "Iniital", actions: {})
it "raises an InvalidAction exception if the action is not valid" do
@initial = Workflows::State.new(name: "Iniital", actions: {})

@card = Workflows::Card.new(id: "123", name: "New order", state: @initial)
@card = Workflows::Card.new(id: "123", name: "New order", state: @initial)

expect { @initial.perform_action "dispatch", card: @card }.to raise_exception(Workflows::State::InvalidAction)
expect { @initial.perform_action "dispatch", card: @card }.to raise_exception(Workflows::State::InvalidAction)
end
end
end
2 changes: 1 addition & 1 deletion workflows.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
spec.require_paths = ["lib"]

spec.add_dependency "dry-configurable"
spec.add_dependency "dry-events"
spec.add_dependency "standard-procedure-plumbing"
spec.add_dependency "dry-struct"
spec.add_dependency "faker"
end

0 comments on commit 4423154

Please sign in to comment.