switcher 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +9 -0
- data/Gemfile +2 -1
- data/README.md +105 -3
- data/Rakefile +6 -0
- data/lib/switcher/adapters/active_record.rb +7 -0
- data/lib/switcher/adapters/object.rb +7 -0
- data/lib/switcher/listener.rb +3 -0
- data/lib/switcher/statement.rb +6 -0
- data/lib/switcher/version.rb +1 -1
- data/spec/fixtures/human.rb +5 -3
- data/spec/switcher_spec.rb +7 -0
- metadata +7 -6
    
        data/.travis.yml
    ADDED
    
    
    
        data/Gemfile
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -1,11 +1,113 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            [](http://travis-ci.org/Ptico/switcher)
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 3 | 
            +
            Switcher is lightweight event-driven state machine.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            ## Basic usage:
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ```ruby
         | 
| 8 | 
            +
            class Package
         | 
| 9 | 
            +
              include Switcher::Object
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              def initialize(weight)
         | 
| 12 | 
            +
                @sent_weight = weight.to_f
         | 
| 13 | 
            +
                @tracking_number = nil
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              switcher :delivery do
         | 
| 17 | 
            +
                state :requested do
         | 
| 18 | 
            +
                  on :send, switch_to: :sent do |ev, num|
         | 
| 19 | 
            +
                    @tracking_number = num
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                state :sent do
         | 
| 24 | 
            +
                  before :receive, call: :check_number
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  on :receive do |ev, number, weight|
         | 
| 27 | 
            +
                    if weight < (@weight - 0.3)
         | 
| 28 | 
            +
                      ev.switch_to :stolen
         | 
| 29 | 
            +
                    else
         | 
| 30 | 
            +
                      ev.switch_to :received
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  after :receive do |ev|
         | 
| 35 | 
            +
                    unpack if delivery_received?
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  on :miss, switch_to: :missed
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                state :received
         | 
| 42 | 
            +
                state :stolen
         | 
| 43 | 
            +
                state :missed
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              def check_number(ev, number, weight)
         | 
| 47 | 
            +
                ev.stop if number.to_s != @tracking_number
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              def unpack
         | 
| 51 | 
            +
                puts "TADA!"
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
            end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            package = Package.new(4.2)
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            package.delivery # => :requested
         | 
| 58 | 
            +
            package.can_send? # => true
         | 
| 59 | 
            +
            package.send! "AB123456CD"
         | 
| 60 | 
            +
            package.delivery # => :sent
         | 
| 61 | 
            +
            package.receive!(3.5, "CD654321AB")
         | 
| 62 | 
            +
            package.delivery # => :sent
         | 
| 63 | 
            +
            package.receive!(4.1, "AB123456CD")
         | 
| 64 | 
            +
            # > TADA!
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            package.delivery # => :received
         | 
| 67 | 
            +
            package.delivery_prev # => :sent
         | 
| 68 | 
            +
            package.delivery_received? # => true
         | 
| 69 | 
            +
            package.can_miss? # => false
         | 
| 70 | 
            +
            ```
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            ## Usage with Active Record:
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            ```ruby
         | 
| 75 | 
            +
            class User < ActiveRecord::Base
         | 
| 76 | 
            +
              include Switcher::ActiveRecord
         | 
| 77 | 
            +
             | 
| 78 | 
            +
              switcher :membership do
         | 
| 79 | 
            +
                state :guest do
         | 
| 80 | 
            +
                  on :approve, switch_to: :member
         | 
| 81 | 
            +
                end
         | 
| 82 | 
            +
                state :member do
         | 
| 83 | 
            +
                  on :ban, switch_to: :banned do |ev, reason|
         | 
| 84 | 
            +
                    ev.stop unless reason
         | 
| 85 | 
            +
                    ban_reason = reason
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
                state :banned do
         | 
| 89 | 
            +
                  on :unban, switch_to: :member
         | 
| 90 | 
            +
                  after :unban do
         | 
| 91 | 
            +
                    ban_reason = nil
         | 
| 92 | 
            +
                  end
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
              end
         | 
| 95 | 
            +
            end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
            user = User.find(5)
         | 
| 98 | 
            +
            user.ban! "Stupid bastard"
         | 
| 99 | 
            +
            user.membership # => :banned
         | 
| 100 | 
            +
            user.save
         | 
| 101 | 
            +
            ```
         | 
| 102 | 
            +
             | 
| 103 | 
            +
            ## More examples:
         | 
| 104 | 
            +
             | 
| 105 | 
            +
            * [Wiki](https://github.com/Ptico/switcher/wiki)
         | 
| 106 | 
            +
            * [Specs](https://github.com/Ptico/switcher/tree/master/spec)
         | 
| 4 107 |  | 
| 5 108 | 
             
            ## TODO:
         | 
| 6 109 |  | 
| 7 110 | 
             
            * 1.8 compat
         | 
| 8 | 
            -
            * Write README
         | 
| 9 111 | 
             
            * Refactoring
         | 
| 10 112 | 
             
            * Events without state
         | 
| 11 113 | 
             
            * Ability to define validations, associations etc. in state (ability to call static methods from object)
         | 
    
        data/Rakefile
    CHANGED
    
    
| @@ -13,6 +13,9 @@ module Switcher | |
| 13 13 | 
             
                      define_method(:"#{spec_name}_prev") { self.instance_variable_get(:"@#{spec_name}_statement").state_prev }
         | 
| 14 14 |  | 
| 15 15 | 
             
                      define_method(:"#{spec_name}") { self.instance_variable_get(:"@#{spec_name}_statement").state_current }
         | 
| 16 | 
            +
                      define_method(:"#{spec_name}=") { nil } # FIXME - raise exception
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                      define_method(:"#{spec_name}_force") { |state| self.instance_variable_get(:"@#{spec_name}_statement").force_state(state.to_sym) }
         | 
| 16 19 |  | 
| 17 20 | 
             
                      events = []
         | 
| 18 21 |  | 
| @@ -62,6 +65,8 @@ module Switcher | |
| 62 65 | 
             
                end
         | 
| 63 66 |  | 
| 64 67 | 
             
                def switch_from(orig, event, *args)
         | 
| 68 | 
            +
                  return false unless (self.respond_to?(:"can_#{event}?") and self.send(:"can_#{event}?"))
         | 
| 69 | 
            +
             | 
| 65 70 | 
             
                  self.class.class_variable_get(:@@__specs__).each do |spc|
         | 
| 66 71 | 
             
                    spc_name = spc.name
         | 
| 67 72 |  | 
| @@ -72,6 +77,8 @@ module Switcher | |
| 72 77 | 
             
                      write_attribute("#{spc_name}_prev", self.send(:"#{spc_name}_prev"))
         | 
| 73 78 | 
             
                    end
         | 
| 74 79 | 
             
                  end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  true
         | 
| 75 82 | 
             
                end
         | 
| 76 83 |  | 
| 77 84 | 
             
              end
         | 
| @@ -20,6 +20,9 @@ module Switcher | |
| 20 20 | 
             
                      define_method(:"#{spec_name}_prev") { self.instance_variable_get(:"@#{spec_name}_statement").state_prev }
         | 
| 21 21 |  | 
| 22 22 | 
             
                      define_method(:"#{spec_name}") { self.instance_variable_get(:"@#{spec_name}_statement").state_current }
         | 
| 23 | 
            +
                      define_method(:"#{spec_name}=") { nil }
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                      define_method(:"#{spec_name}_force") { |state| self.instance_variable_get(:"@#{spec_name}_statement").force_state(state.to_sym) }
         | 
| 23 26 |  | 
| 24 27 | 
             
                      events = []
         | 
| 25 28 |  | 
| @@ -57,9 +60,13 @@ module Switcher | |
| 57 60 | 
             
                end
         | 
| 58 61 |  | 
| 59 62 | 
             
                def switch_from(orig, event, *args)
         | 
| 63 | 
            +
                  return false unless (self.respond_to?(:"can_#{event}?") and self.send(:"can_#{event}?"))
         | 
| 64 | 
            +
             | 
| 60 65 | 
             
                  self.class.class_variable_get(:@@__specs__).each do |spc|
         | 
| 61 66 | 
             
                    self.instance_variable_get(:"@#{spc.name}_statement").publish(event, orig, args)
         | 
| 62 67 | 
             
                  end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  true
         | 
| 63 70 | 
             
                end
         | 
| 64 71 | 
             
              end
         | 
| 65 72 | 
             
            end
         | 
    
        data/lib/switcher/listener.rb
    CHANGED
    
    | @@ -28,6 +28,9 @@ module Switcher | |
| 28 28 |  | 
| 29 29 | 
             
                def trigger(event, facade, instance, args)
         | 
| 30 30 | 
             
                  if ev = @events[event]
         | 
| 31 | 
            +
                    if ev[:options][:call]
         | 
| 32 | 
            +
                      instance.instance_exec(facade, *args) { |facad, *arguments| self.send(ev[:options][:call], facad, *arguments) }
         | 
| 33 | 
            +
                    end
         | 
| 31 34 | 
             
                    instance.instance_exec(facade, *args, &ev[:callback]) if ev[:callback].respond_to?(:call)
         | 
| 32 35 | 
             
                    if !facade.stopped && ev[:options][:allow_switch] && switch_to = ev[:options][:switch_to]
         | 
| 33 36 | 
             
                      facade.switch_to(switch_to)
         | 
    
        data/lib/switcher/statement.rb
    CHANGED
    
    | @@ -51,6 +51,12 @@ module Switcher | |
| 51 51 | 
             
                  end
         | 
| 52 52 | 
             
                end
         | 
| 53 53 |  | 
| 54 | 
            +
                def force_state(state)
         | 
| 55 | 
            +
                  return unless @spec.states_list.include?(state.to_sym) # FIXME - raise exception
         | 
| 56 | 
            +
                  @state_prev    = state_current
         | 
| 57 | 
            +
                  @state_current = state.to_sym
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 54 60 | 
             
              private
         | 
| 55 61 |  | 
| 56 62 | 
             
                def set_state(facade)
         | 
    
        data/lib/switcher/version.rb
    CHANGED
    
    
    
        data/spec/fixtures/human.rb
    CHANGED
    
    | @@ -68,9 +68,7 @@ class Human | |
| 68 68 | 
             
                end
         | 
| 69 69 |  | 
| 70 70 | 
             
                state :civil do
         | 
| 71 | 
            -
                  on :find_job, switch_to: :manager | 
| 72 | 
            -
                    ev.restrict unless cv
         | 
| 73 | 
            -
                  end
         | 
| 71 | 
            +
                  on :find_job, call: :employment, switch_to: :manager
         | 
| 74 72 | 
             
                end
         | 
| 75 73 |  | 
| 76 74 | 
             
                state :manager do
         | 
| @@ -89,4 +87,8 @@ class Human | |
| 89 87 | 
             
              def hard_to?(learn=false)
         | 
| 90 88 | 
             
                !!learn
         | 
| 91 89 | 
             
              end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
              def employment(ev, cv)
         | 
| 92 | 
            +
                ev.restrict unless cv
         | 
| 93 | 
            +
              end
         | 
| 92 94 | 
             
            end
         | 
    
        data/spec/switcher_spec.rb
    CHANGED
    
    | @@ -59,6 +59,13 @@ describe Switcher do | |
| 59 59 | 
             
                  person.state_manager?.should be_true
         | 
| 60 60 | 
             
                  person.state_civil?.should   be_false
         | 
| 61 61 | 
             
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                it "should force state if needed" do
         | 
| 64 | 
            +
                  crashed_car = Car.new
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  crashed_car.state_force(:damaged)
         | 
| 67 | 
            +
                  crashed_car.state.should eq(:damaged)
         | 
| 68 | 
            +
                end
         | 
| 62 69 | 
             
              end
         | 
| 63 70 |  | 
| 64 71 | 
             
              describe "Multiple states" do
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: switcher
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.2.0
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -9,11 +9,11 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2012-03- | 
| 12 | 
            +
            date: 2012-03-05 00:00:00.000000000Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: rake
         | 
| 16 | 
            -
              requirement: & | 
| 16 | 
            +
              requirement: &70322138874120 !ruby/object:Gem::Requirement
         | 
| 17 17 | 
             
                none: false
         | 
| 18 18 | 
             
                requirements:
         | 
| 19 19 | 
             
                - - ! '>='
         | 
| @@ -21,10 +21,10 @@ dependencies: | |
| 21 21 | 
             
                    version: '0'
         | 
| 22 22 | 
             
              type: :development
         | 
| 23 23 | 
             
              prerelease: false
         | 
| 24 | 
            -
              version_requirements: * | 
| 24 | 
            +
              version_requirements: *70322138874120
         | 
| 25 25 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 26 26 | 
             
              name: rspec
         | 
| 27 | 
            -
              requirement: & | 
| 27 | 
            +
              requirement: &70322138873700 !ruby/object:Gem::Requirement
         | 
| 28 28 | 
             
                none: false
         | 
| 29 29 | 
             
                requirements:
         | 
| 30 30 | 
             
                - - ! '>='
         | 
| @@ -32,7 +32,7 @@ dependencies: | |
| 32 32 | 
             
                    version: '0'
         | 
| 33 33 | 
             
              type: :development
         | 
| 34 34 | 
             
              prerelease: false
         | 
| 35 | 
            -
              version_requirements: * | 
| 35 | 
            +
              version_requirements: *70322138873700
         | 
| 36 36 | 
             
            description: Switcher is simple, event-driven state machine
         | 
| 37 37 | 
             
            email:
         | 
| 38 38 | 
             
            - andrey@aejis.eu
         | 
| @@ -41,6 +41,7 @@ extensions: [] | |
| 41 41 | 
             
            extra_rdoc_files: []
         | 
| 42 42 | 
             
            files:
         | 
| 43 43 | 
             
            - .gitignore
         | 
| 44 | 
            +
            - .travis.yml
         | 
| 44 45 | 
             
            - Gemfile
         | 
| 45 46 | 
             
            - Guardfile
         | 
| 46 47 | 
             
            - README.md
         |