Skip to content
This repository has been archived by the owner on Jul 29, 2021. It is now read-only.

Commit

Permalink
Add YARD documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
rtwomey committed Nov 3, 2012
1 parent 10c1ac4 commit 74248e4
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -2,6 +2,9 @@
log/*.log
pkg/
rdoc/
.DS_Store
.yardoc/
doc/
test/dummy/db/*.sqlite3
test/dummy/log/*.log
test/dummy/tmp/
Expand Down
4 changes: 4 additions & 0 deletions .yardopts
@@ -0,0 +1,4 @@
lib/**/*.rb
-
README.md
MIT-LICENSE
4 changes: 4 additions & 0 deletions Gemfile.lock
Expand Up @@ -7,6 +7,7 @@ GEM
remote: http://rubygems.org/
specs:
diff-lcs (1.1.3)
redcarpet (2.2.2)
rspec (2.11.0)
rspec-core (~> 2.11.0)
rspec-expectations (~> 2.11.0)
Expand All @@ -15,10 +16,13 @@ GEM
rspec-expectations (2.11.3)
diff-lcs (~> 1.1.3)
rspec-mocks (2.11.3)
yard (0.8.3)

PLATFORMS
ruby

DEPENDENCIES
redcarpet (~> 2.2.2)
rspec (~> 2.0)
stately!
yard (~> 0.8.3)
60 changes: 58 additions & 2 deletions lib/stately.rb
Expand Up @@ -2,9 +2,52 @@
require 'stately/state'

module Stately
# An InvalidTransition is an error that is raised when attempting to transition from a state
# that's not allowable, based on the Stately::State DSL definitions allow_from and prevent_from.
class InvalidTransition < StandardError
end

# Define a new Stately state machine.
#
# As an example, let's say you have an Order object and you'd like an elegant state machine for
# it. Here's one way you might set it up:
#
# Class Order do
# stately start: :processing do
# state :completed do
# prevent_from :refunded
#
# before_transition from: :processing, do: :calculate_total
# after_transition do: :email_receipt
#
# validate :validates_credit_card
# end
#
# state :invalid do
# prevent_from :completed, :refunded
# end
#
# state :refunded do
# allow_from :completed
#
# after_transition do: :email_receipt
# end
# end
# end
#
# This example is doing quite a few things, paraphrased as:
#
# * It sets up a new state machine using the default state attribute on Order to store the
# current state. It also indicates the initial state should be :processing.
# * It defines three states: :completed, :refunded, and :invalid
# * Order can transition to the completed state from all but the refunded state. Similar
# definitions are setup for the other two states.
# * Callbacks are setup using before_transition and after_transition
# * Validations are added. If a validation fails, it prevents the transition.
#
# Stately tries hard not to surprise you. In a typical Stately implementation, you'll always have
# an after_transition, primarily to call save (or whatever the equivalent is to store the
# instance's current state).
def stately(*opts, &block)
options = opts.last.is_a?(Hash) ? opts.last : {}
options[:attr] ||= :state
Expand All @@ -15,23 +58,37 @@ def stately(*opts, &block)
include Stately::InstanceMethods
end

# Get the current Stately::Machine object
def self.stately_machine
@@stately_machine
end

# Get the current Stately::Machine object
def stately_machine
@@stately_machine
end

# Set the current Stately::Machine object
def self.stately_machine=(obj)
@@stately_machine = obj
end

# Set the current Stately::Machine object
def stately_machine=(obj)
@@stately_machine = obj
end

module InstanceMethods
# Sets up an object with Stately. The DSL is parsed and the Stately::Machine is initialized.
#
# When an object is first initialized, Stately automatically sets the state attribute to the
# start state.
#
# Additionally, a method is defined for each of the state's actions. These methods are used to
# transition between states. If you have a state named 'completed', Stately will infer the
# action to be 'complete' and define a method named 'complete'. You can then call 'complete' on
# the object to transition into the completed state.

def InstanceMethods.included(klass)
klass.class_eval do
alias_method :init_instance, :initialize
Expand All @@ -48,6 +105,7 @@ def initialize(*args)
end
end

# @return [Array<String>] a list of state names.
def states
stately_machine.states.map(&:name)
end
Expand Down Expand Up @@ -125,9 +183,7 @@ def write_attribute(attr, val)
end

def valid_transition_to?(state)
# check this is an allowed state to transition from
if allowed_state_transition?(state)
# check validations
if state.validations.nil? || state.validations.empty?
true
else
Expand Down
2 changes: 2 additions & 0 deletions lib/stately/core_ext.rb
@@ -1,3 +1,5 @@
# Includes Stately on Ruby's Object.

Object.class_eval do
include Stately
end
8 changes: 8 additions & 0 deletions lib/stately/machine.rb
@@ -1,13 +1,21 @@
module Stately
# A Stately::Machine is a container for Stately::States.
class Machine
attr_reader :start, :state_attr, :states

# Sets up a new instance of Stately::Machine
def initialize(attr_name, start)
@state_attr = attr_name
@start = start
@states = [State.new(@start)]
end

# Define a new Stately::State and add it to this Stately::Machine.
#
# @param [String] name The name of the state. This is also stored in the instance object's
# state attribute.
# @param [Hash] opts Optionally, a method name can be defined as this state's action, if it
# can't be inferred from the name.
def state(name, opts={}, &block)
@states.delete_if { |s| s.name == name }

Expand Down
14 changes: 14 additions & 0 deletions lib/stately/state.rb
@@ -1,9 +1,21 @@
module Stately
# A Stately::State object contains the configuration and other information about a defined
# state.
#
# It's made up of a name (which is saved to the parent instance's state attribute), the
# name of an action (which is a method called to transition into this state), and a DSL to
# define allowed transitions, callbacks, and validations.

class State
attr_reader :action, :name
attr_reader :allow_from_states, :prevent_from_states
attr_reader :after_transitions, :before_transitions, :validations

# Sets up and returns a new Stately::State object.
#
# @param [String] name The name of the state
# @param [String] action The method name that's called to transition to this state. Some method
# names can be inferred based on the state's name.
def initialize(name, action=nil, &block)
@action = (action || guess_action_for(name)).to_s
@name = name
Expand All @@ -27,10 +39,12 @@ def initialize(name, action=nil, &block)
end
end

# @return [String] The state name as a string
def to_s
@name.to_s
end

# @return [Symbol] The state name as a string
def to_sym
@name.to_sym
end
Expand Down
2 changes: 2 additions & 0 deletions stately.gemspec
Expand Up @@ -13,7 +13,9 @@ Gem::Specification.new do |s|
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {spec}/*`.split("\n")

s.add_development_dependency 'redcarpet', '~> 2.2.2'
s.add_development_dependency 'rspec', '~> 2.0'
s.add_development_dependency 'yard', '~> 0.8.3'

s.required_ruby_version = Gem::Requirement.new('>= 1.9.2')
s.require_paths = ['lib']
Expand Down

0 comments on commit 74248e4

Please sign in to comment.