mutator 0.0.2 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 266ddb73a57d444d63b9f9460df79a5eb8d19089
4
- data.tar.gz: 8597a08ee353f97a42a3d7469bf187c494ce1723
3
+ metadata.gz: d69e47c148240cf6a7f6000f259980b0ef39caa6
4
+ data.tar.gz: b1aa0ed144d14bd76ab91a1884f7d005519afbb3
5
5
  SHA512:
6
- metadata.gz: 0f84541de39489ad4dab75b699e895b9f552cf86b0302cc02c93865d64001f43a646d7726515432bb29656df2a6987546122c7906c79047606a1ee0f8f73fb8e
7
- data.tar.gz: 0e640ac518c45bf09731a77201096c1d57d860fc34ca8e7c90dc85f58e93222fee50d1c9fd9e4b355bf7adc38705f0d274ab2eebd86b3f4779817a813d1d398b
6
+ metadata.gz: ae7fdbe701040f8116984b8ed5222231f97333dfa1b62068eec392b3aa9ea5f05cf40ed60d3a33b5ab3616f1175ef910e5b9d6ddeeb268b22806f4d7f2865fc5
7
+ data.tar.gz: 94afab94f0b66e4b76d1c6f5086fb5080db2040eab6215dc55254ee0b365932a133f0fcdc7597497ba659eead9d185c19d6591e6c39ca117498a2028cc3572c9
data/.hound.yml ADDED
@@ -0,0 +1,5 @@
1
+ StringLiterals:
2
+ Enabled: false
3
+
4
+ AccessModifierIndentation:
5
+ EnforcedStyle: outdent
data/README.md CHANGED
@@ -1,3 +1,8 @@
1
+ [![Gem Version](https://badge.fury.io/rb/mutator.png)](http://badge.fury.io/rb/mutator)
2
+ [![Build Status](https://travis-ci.org/ericroberts/mutator.png?branch=master)](https://travis-ci.org/ericroberts/mutator)
3
+ [![Code Climate](https://codeclimate.com/github/ericroberts/mutator.png)](https://codeclimate.com/github/ericroberts/mutator)
4
+ [![Coverage Status](https://coveralls.io/repos/ericroberts/mutator/badge.png?branch=master)](https://coveralls.io/r/ericroberts/mutator?branch=master)
5
+
1
6
  # Mutator
2
7
 
3
8
  Yes, this is another state machine gem. Why? Well, among the major state machine gems I found, they didn't quite do what I was looking for. So I wrote my own. Plus, reinventing the wheel is fun!
@@ -46,7 +51,7 @@ end
46
51
 
47
52
  ### So how do I use it?
48
53
 
49
- Transition looks like this:
54
+ Transitions look like this:
50
55
 
51
56
  ``` ruby
52
57
  wonder = Wonder.new
@@ -5,7 +5,7 @@ module Mutator
5
5
  end
6
6
 
7
7
  def self.included(base)
8
- "Mutator::#{base.name}".constantize.states.each do |state|
8
+ Mutator.const_get(base.name, false).states.each do |state|
9
9
  base.send(:define_singleton_method, state) do
10
10
  self.where(state: state)
11
11
  end
@@ -15,7 +15,7 @@ module Mutator
15
15
  protected
16
16
 
17
17
  def machine_class
18
- "Mutator::#{self.class.name}".constantize
18
+ Mutator.const_get(self.class.name, false)
19
19
  end
20
20
  end
21
21
  end
@@ -14,11 +14,11 @@ module Mutator
14
14
  stateholder.state
15
15
  end
16
16
 
17
- def transition(to:, success: lambda { |_transition| }, failure: lambda { |_transition| })
18
- transition = Transition.new(to: to, from: current_state, machine: self)
17
+ def transition(options)
18
+ options = extract(options)
19
+ success, failure, transition = options.values
19
20
 
20
- if transition.valid?
21
- stateholder.state = to
21
+ if transition.call
22
22
  success.call(transition)
23
23
  true
24
24
  else
@@ -28,8 +28,9 @@ module Mutator
28
28
  end
29
29
 
30
30
  def self.states
31
- self.transitions.map do |t|
32
- [t[:to], t[:from]]
31
+ self.transitions.map do |transition|
32
+ to, from = transition[:to], transition[:from]
33
+ [to, from]
33
34
  end.flatten.uniq
34
35
  end
35
36
 
@@ -40,5 +41,22 @@ module Mutator
40
41
  def transitions
41
42
  self.class.transitions
42
43
  end
44
+
45
+ protected
46
+
47
+ def extract(options)
48
+ to = options[:to]
49
+ fail ArgumentError, 'must provide state to transition to' unless to
50
+
51
+ {
52
+ success: lambda { |_| },
53
+ failure: lambda { |_| },
54
+ transition: Transition.new(
55
+ to: to,
56
+ from: current_state,
57
+ machine: self
58
+ )
59
+ }.merge(options)
60
+ end
43
61
  end
44
62
  end
@@ -2,23 +2,42 @@ module Mutator
2
2
  class Transition
3
3
  attr_reader :to, :from, :machine
4
4
 
5
- def initialize(to:, from:, machine:)
6
- @to, @from, @machine = to, from, machine
5
+ def initialize(opts)
6
+ require_parameters!(opts)
7
+ @to, @from, @machine = opts[:to], opts[:from], opts[:machine]
8
+ end
9
+
10
+ def call
11
+ stateholder.state = to if valid?
7
12
  end
8
13
 
9
14
  def valid?
10
- transitions.present?
15
+ transitions.length > 0
11
16
  end
12
17
 
13
18
  def stateholder
14
19
  machine.stateholder
15
20
  end
16
21
 
22
+ def ==(other)
23
+ to == other.to && from == other.from && machine == other.machine
24
+ end
25
+
26
+ def eql?(other)
27
+ public_send(:==, other)
28
+ end
29
+
17
30
  protected
18
31
 
19
32
  def transitions
20
- machine.transitions.select do |t|
21
- t[:to] == to && t[:from].include?(from)
33
+ machine.transitions.select do |transition|
34
+ transition[:to] == to && transition[:from].include?(from)
35
+ end
36
+ end
37
+
38
+ def require_parameters!(opts)
39
+ [:to, :from, :machine].each do |attr|
40
+ fail ArgumentError, "must provide #{attr}" unless opts[attr]
22
41
  end
23
42
  end
24
43
  end
@@ -1,3 +1,3 @@
1
1
  module Mutator
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.0"
3
3
  end
data/mutator.gemspec CHANGED
@@ -22,4 +22,6 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency "rake"
23
23
  spec.add_development_dependency "rspec", "~> 3.0"
24
24
  spec.add_development_dependency "coveralls"
25
+ spec.add_development_dependency "awesome_print"
26
+ spec.add_development_dependency "pry"
25
27
  end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ require 'support/test_classes'
3
+
4
+ describe Mutator::Helpers do
5
+ subject { Mutator::Helpers }
6
+
7
+ describe '.included' do
8
+ let(:base) { Stateholder }
9
+
10
+ it 'should not error' do
11
+ expect { subject.included(base) }.to_not raise_error
12
+ end
13
+ end
14
+
15
+ context 'when included' do
16
+ subject { Stateholder.new }
17
+
18
+ describe '#machine' do
19
+ it 'should respond to it' do
20
+ expect(subject).to respond_to :machine
21
+ end
22
+
23
+ it 'should return a Mutator::Stateholder object' do
24
+ expect(subject.machine).to be_a Mutator::Stateholder
25
+ end
26
+ end
27
+
28
+ [:initial_state, :second_state, :third_state].each do |state|
29
+ describe ".#{state}" do
30
+ it 'should respond to the method' do
31
+ expect(subject.class).to respond_to state
32
+ end
33
+
34
+ it 'should call where on the stateholder' do
35
+ expect(subject.class).to receive(:where).
36
+ with(state: state).
37
+ and_return(Stateholder.new)
38
+ subject.class.public_send(state)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,167 @@
1
+ require 'spec_helper'
2
+ require 'support/test_classes'
3
+
4
+ describe Mutator::Stateholder do
5
+ subject { Mutator::Stateholder.new(stateholder) }
6
+ let(:stateholder) { Stateholder.new }
7
+
8
+ describe '#initialize' do
9
+ it 'should not error' do
10
+ expect { subject }.to_not raise_error
11
+ end
12
+ end
13
+
14
+ describe '#stateholder' do
15
+ it 'should return the stateholder it was passed' do
16
+ expect(subject.stateholder).to eq stateholder
17
+ end
18
+ end
19
+
20
+ describe '#valid?' do
21
+ it 'should be valid if the state exists in states list' do
22
+ subject.stateholder.state = :initial_state
23
+ expect(subject).to be_valid
24
+ end
25
+
26
+ it 'should not be valid for a state not in states list' do
27
+ subject.stateholder.state = :foobar
28
+ expect(subject).to_not be_valid
29
+ end
30
+ end
31
+
32
+ describe '#current_state' do
33
+ it 'should return the stateholder state' do
34
+ expect(subject.current_state).to eq stateholder.state
35
+ end
36
+ end
37
+
38
+ describe '#transition' do
39
+ let(:transition) do
40
+ Mutator::Transition.new(to: to, from: from, machine: subject)
41
+ end
42
+
43
+ context 'arguments' do
44
+ it 'should raise an exception if you do not provide to' do
45
+ expect { subject.transition }.to raise_error ArgumentError
46
+ end
47
+
48
+ it 'should not raise if to is provided' do
49
+ expect { subject.transition to: :any_state }.to_not raise_error
50
+ end
51
+ end
52
+
53
+ context 'valid transition' do
54
+ let(:from) { :initial_state }
55
+ let(:to) { :second_state }
56
+
57
+ before { subject.stateholder.state = from }
58
+
59
+ it 'should update state' do
60
+ expect(subject.stateholder.state).to eq from
61
+ subject.transition(to: to)
62
+ expect(subject.stateholder.state).to eq to
63
+ end
64
+
65
+ it 'should return true' do
66
+ expect(subject.transition(to: to)).to be true
67
+ end
68
+
69
+ context 'with success policy' do
70
+ let(:success) { double }
71
+
72
+ before { allow(success).to receive(:call).and_return(true) }
73
+
74
+ it 'should still update state' do
75
+ expect(subject.stateholder.state).to eq from
76
+ subject.transition(to: to, success: success)
77
+ expect(subject.stateholder.state).to eq to
78
+ end
79
+
80
+ it 'should still return true' do
81
+ expect(subject.transition(to: to, success: success)).to be true
82
+ end
83
+
84
+ it 'should call the success policy' do
85
+ expect(success).to receive(:call).and_return(true)
86
+ subject.transition(to: to, success: success)
87
+ end
88
+
89
+ it 'should call the success policy with transition' do
90
+ expect(success).to receive(:call).with(transition).and_return(true)
91
+ subject.transition(to: to, success: success)
92
+ end
93
+ end
94
+ end
95
+
96
+ context 'invalid transition' do
97
+ let(:from) { :initial_state }
98
+ let(:to) { :third_state }
99
+
100
+ before { subject.stateholder.state = from }
101
+
102
+ it 'should not update state' do
103
+ expect(subject.stateholder.state).to eq from
104
+ subject.transition(to: to)
105
+ expect(subject.stateholder.state).to eq from
106
+ end
107
+
108
+ it 'should return false' do
109
+ expect(subject.transition(to: to)).to be false
110
+ end
111
+
112
+ context 'with failure policy' do
113
+ let(:failure) { double }
114
+
115
+ before { allow(failure).to receive(:call).and_return(true) }
116
+
117
+ it 'should still not update state' do
118
+ expect(subject.stateholder.state).to eq from
119
+ subject.transition(to: to, failure: failure)
120
+ expect(subject.stateholder.state).to eq from
121
+ end
122
+
123
+ it 'should still return false' do
124
+ expect(subject.transition(to: to, failure: failure)).to be false
125
+ end
126
+
127
+ it 'should call the failure policy' do
128
+ expect(failure).to receive(:call).and_return(true)
129
+ subject.transition(to: to, failure: failure)
130
+ end
131
+
132
+ it 'should call the failure policy with transition' do
133
+ expect(failure).to receive(:call).with(transition).and_return(true)
134
+ subject.transition(to: to, failure: failure)
135
+ end
136
+
137
+ context 'when policy is to raise an exception' do
138
+ before { expect(failure).to receive(:call).and_raise(Exception) }
139
+
140
+ it 'should raise an exception' do
141
+ expect { subject.transition(to: to, failure: failure) }.to raise_error Exception
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ describe '#states' do
149
+ it 'should respond to states' do
150
+ expect(subject).to respond_to :states
151
+ end
152
+
153
+ it 'should extract states from transitions' do
154
+ expect(subject.states.sort).to eq [
155
+ :initial_state,
156
+ :second_state,
157
+ :third_state
158
+ ].sort
159
+ end
160
+ end
161
+
162
+ describe '#transitions' do
163
+ it 'should respond to' do
164
+ expect(subject).to respond_to :transitions
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+ require 'support/test_classes'
3
+
4
+ describe Mutator::Transition do
5
+ subject { Mutator::Transition.new(to: to, from: from, machine: machine) }
6
+ let(:to) { :initial_state }
7
+ let(:from) { :second_state }
8
+ let(:machine) { Mutator::Stateholder.new(stateholder) }
9
+ let(:stateholder) { Stateholder.new }
10
+
11
+ describe '#initialize' do
12
+ it 'should not error' do
13
+ expect { subject }.to_not raise_error
14
+ end
15
+
16
+ context 'arguments' do
17
+ [:to, :from, :machine].each do |attr|
18
+ it "should require you to pass #{attr}" do
19
+ args = { to: :something, from: :something, machine: :something }
20
+ args.delete attr
21
+ expect { subject.class.new(args) }.to raise_error ArgumentError, "must provide #{attr}"
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ [:to, :from, :machine].each do |attr|
28
+ describe "##{attr}" do
29
+ it 'should be set to what it was passed' do
30
+ expect(subject.public_send(attr)).to eq public_send(attr)
31
+ end
32
+ end
33
+ end
34
+
35
+ describe '#call' do
36
+ context 'transition is valid' do
37
+ let(:to) { :second_state }
38
+ let(:from) { :initial_state }
39
+
40
+ it 'should return the new state' do
41
+ expect(subject.call).to eq :second_state
42
+ end
43
+ end
44
+
45
+ context 'transition is invalid' do
46
+ let(:to) { :third_state }
47
+ let(:from) { :initial_state }
48
+
49
+ it 'should return the new state' do
50
+ expect(subject.call).to be nil
51
+ end
52
+ end
53
+ end
54
+
55
+ describe '#valid?' do
56
+ context 'valid transition' do
57
+ let(:to) { :second_state }
58
+ let(:from) { :initial_state }
59
+
60
+ it 'should be true' do
61
+ expect(subject).to be_valid
62
+ end
63
+ end
64
+
65
+ context 'invalid transition' do
66
+ let(:to) { :third_state }
67
+ let(:from) { :initial_state }
68
+
69
+ it 'should be false' do
70
+ expect(subject).to_not be_valid
71
+ end
72
+ end
73
+ end
74
+
75
+ describe '#stateholder' do
76
+ it 'should return the machine stateholder' do
77
+ expect(subject.stateholder).to eq machine.stateholder
78
+ end
79
+ end
80
+
81
+ shared_examples 'equal' do |method|
82
+ it 'should consider itself equal to another transition that is the same' do
83
+ transition1 = subject.class.new(to: to, from: from, machine: machine)
84
+ transition2 = subject.class.new(to: to, from: from, machine: machine)
85
+
86
+ expect(transition1.public_send(method, transition2)).to be true
87
+ end
88
+ end
89
+
90
+ describe '#==' do
91
+ it_should_behave_like 'equal', :==
92
+ end
93
+
94
+ describe '#eql?' do
95
+ it_should_behave_like 'equal', :eql?
96
+ end
97
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,2 +1,4 @@
1
1
  require 'coveralls'
2
2
  Coveralls.wear!
3
+
4
+ require 'mutator'
@@ -0,0 +1,23 @@
1
+ module Mutator
2
+ class Stateholder < Machine
3
+ def self.transitions
4
+ [
5
+ { from: [:initial_state], to: :second_state },
6
+ { from: [:second_state], to: :third_state }
7
+ ]
8
+ end
9
+ end
10
+ end
11
+
12
+ class Stateholder
13
+ include Mutator::Helpers
14
+
15
+ attr_writer :state
16
+
17
+ def state
18
+ @state ||= :initial_state
19
+ end
20
+
21
+ def self.where(*)
22
+ end
23
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mutator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Roberts
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-11 00:00:00.000000000 Z
11
+ date: 2014-08-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,34 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: awesome_print
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
69
97
  description: Yet another state machine gem. I didn't find one I liked, so I made one.
70
98
  I probably didn't look hard enough.
71
99
  email:
@@ -75,6 +103,7 @@ extensions: []
75
103
  extra_rdoc_files: []
76
104
  files:
77
105
  - ".gitignore"
106
+ - ".hound.yml"
78
107
  - ".rspec"
79
108
  - ".travis.yml"
80
109
  - Gemfile
@@ -87,7 +116,11 @@ files:
87
116
  - lib/mutator/transition.rb
88
117
  - lib/mutator/version.rb
89
118
  - mutator.gemspec
119
+ - spec/lib/mutator/helpers_spec.rb
120
+ - spec/lib/mutator/machine_spec.rb
121
+ - spec/lib/mutator/transition_spec.rb
90
122
  - spec/spec_helper.rb
123
+ - spec/support/test_classes.rb
91
124
  homepage: https://github.com/ericroberts/mutator
92
125
  licenses:
93
126
  - MIT
@@ -113,4 +146,8 @@ signing_key:
113
146
  specification_version: 4
114
147
  summary: Mutator is just another state machine gem.
115
148
  test_files:
149
+ - spec/lib/mutator/helpers_spec.rb
150
+ - spec/lib/mutator/machine_spec.rb
151
+ - spec/lib/mutator/transition_spec.rb
116
152
  - spec/spec_helper.rb
153
+ - spec/support/test_classes.rb