simple_states 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ platforms :mri_18 do
7
+ gem "require_relative", "~> 1.0.1"
8
+ gem 'ruby-debug'
9
+ end
10
+
11
+ platforms :mri_19 do
12
+ gem 'ruby-debug19', :platforms => :mri_19 unless RUBY_VERSION == '1.9.3'
13
+ end
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,48 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ simple_states (0.0.1)
5
+ activesupport
6
+ hashr (~> 0.0.10)
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ activesupport (3.0.9)
12
+ archive-tar-minitar (0.5.2)
13
+ columnize (0.3.4)
14
+ hashr (0.0.10)
15
+ linecache (0.46)
16
+ rbx-require-relative (> 0.0.4)
17
+ linecache19 (0.5.12)
18
+ ruby_core_source (>= 0.1.4)
19
+ mocha (0.9.12)
20
+ rbx-require-relative (0.0.5)
21
+ require_relative (1.0.2)
22
+ ruby-debug (0.10.4)
23
+ columnize (>= 0.1)
24
+ ruby-debug-base (~> 0.10.4.0)
25
+ ruby-debug-base (0.10.4)
26
+ linecache (>= 0.3)
27
+ ruby-debug-base19 (0.11.25)
28
+ columnize (>= 0.3.1)
29
+ linecache19 (>= 0.5.11)
30
+ ruby_core_source (>= 0.1.4)
31
+ ruby-debug19 (0.11.6)
32
+ columnize (>= 0.3.1)
33
+ linecache19 (>= 0.5.11)
34
+ ruby-debug-base19 (>= 0.11.19)
35
+ ruby_core_source (0.1.5)
36
+ archive-tar-minitar (>= 0.5.2)
37
+ test_declarative (0.0.5)
38
+
39
+ PLATFORMS
40
+ ruby
41
+
42
+ DEPENDENCIES
43
+ mocha
44
+ require_relative (~> 1.0.1)
45
+ ruby-debug
46
+ ruby-debug19
47
+ simple_states!
48
+ test_declarative
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT LICENSE
2
+
3
+ Copyright (c) Sven Fuchs <svenfuchs@artweb-design.de>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/NOTES.md ADDED
@@ -0,0 +1 @@
1
+ make it so that callbacks can be cancelled by returning false, maybe look into activesupport callbacks?
data/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # simple\_states
2
+
3
+ A super-slim (~200 loc) statemachine-like support library focussed on use in Travis CI.
4
+
5
+ More docs coming soon, checkout the tests.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'lib' << 'test'
6
+ t.pattern = 'test/**/*_test.rb'
7
+ t.verbose = false
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,70 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/core_ext/class/inheritable_attributes'
3
+ require 'active_support/core_ext/kernel/singleton_class'
4
+ require 'active_support/core_ext/object/try'
5
+
6
+ module SimpleStates
7
+ class TransitionException < RuntimeError; end
8
+
9
+ autoload :Event, 'simple_states/event'
10
+ autoload :States, 'simple_states/states'
11
+
12
+ extend ActiveSupport::Concern
13
+
14
+ included do
15
+ class_inheritable_accessor :state_names, :initial_state, :events
16
+ self.initial_state = :created
17
+ self.events = []
18
+ end
19
+
20
+ module ClassMethods
21
+ def new(*)
22
+ super.tap { |object| States.init(object) }
23
+ end
24
+
25
+ def allocate
26
+ super.tap { |object| States.init(object) }
27
+ end
28
+
29
+ def states(*args)
30
+ if args.empty?
31
+ self.state_names ||= add_states(self.initial_state)
32
+ else
33
+ options = args.last.is_a?(Hash) ? args.pop : {}
34
+ self.initial_state = options[:initial].to_sym if options.key?(:initial)
35
+ add_states(*[self.initial_state].concat(args))
36
+ end
37
+ end
38
+
39
+ def add_states(*states)
40
+ self.state_names = (self.state_names || []).concat(states.compact.map(&:to_sym)).uniq
41
+ end
42
+
43
+ def event(name, options = {})
44
+ add_states(options[:from], options[:to])
45
+ self.events << Event.new(name, options)
46
+ end
47
+ end
48
+
49
+ attr_reader :past_states
50
+
51
+ def past_states
52
+ @past_states ||= []
53
+ end
54
+
55
+ def state?(state, include_past = false)
56
+ include_past ? was_state?(state) : self.state.try(:to_sym) == state.to_sym
57
+ end
58
+
59
+ def was_state?(state)
60
+ past_states.concat([self.state.try(:to_sym)]).compact.include?(state.to_sym)
61
+ end
62
+
63
+ def respond_to?(method, include_private = false)
64
+ method.to_s =~ /(was_|^)(#{self.class.states.join('|')})\?$/ && self.class.state_names.include?($2.to_sym) || super
65
+ end
66
+
67
+ def method_missing(method, *args, &block)
68
+ method.to_s =~ /(was_|^)(#{self.class.states.join('|')})\?$/ ? send(:"#{$1}state?", $2, *args) : super
69
+ end
70
+ end
@@ -0,0 +1,87 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+ require 'hashr'
3
+
4
+ module SimpleStates
5
+ class Event
6
+ attr_accessor :name, :options
7
+
8
+ def initialize(name, options = {})
9
+ @name = name
10
+ @options = Hashr.new(options) do
11
+ def except
12
+ self[:except]
13
+ end
14
+ end
15
+ end
16
+
17
+ def saving
18
+ @saving = true
19
+ yield.tap { @saving = false }
20
+ end
21
+
22
+ def call(object, *args)
23
+ return if skip?(object, args)
24
+
25
+ raise_invalid_transition(object) unless can_transition?(object)
26
+ run_callbacks(object, :before, args)
27
+
28
+ yield.tap do
29
+ set_state(object)
30
+ run_callbacks(object, :after, args)
31
+ object.save! if @saving
32
+ end
33
+ end
34
+
35
+ def merge(other)
36
+ other.options.each do |key, value|
37
+ options[key] = [options[key], value].compact unless key == :to
38
+ end
39
+ end
40
+
41
+ protected
42
+
43
+ def skip?(object, args)
44
+ result = false
45
+ result ||= !send_methods(object, options.if, args) if options.if?
46
+ result ||= send_methods(object, options.except, args) if options.except?
47
+ result
48
+ end
49
+
50
+ def can_transition?(object)
51
+ object.state && Array(options.from).include?(object.state)
52
+ end
53
+
54
+ def raise_invalid_transition(object)
55
+ raise TransitionException, "#{object.inspect} can not receive event #{name.inspect} while in state #{object.state.inspect}."
56
+ end
57
+
58
+ def run_callbacks(object, type, args)
59
+ send_methods(object, options.send(type), args)
60
+ end
61
+
62
+ def set_state(object)
63
+ if state = options.to
64
+ object.past_states << object.state if object.state
65
+ object.state = state.to_sym
66
+ object.send(:"#{state}_at=", Time.now) if object.respond_to?(:"#{state}_at=")
67
+ object.save! if @saving
68
+ end
69
+ end
70
+
71
+ def send_methods(object, methods, args)
72
+ Array(methods).inject(false) { |result, method| result | send_method(object, method, args) } if methods
73
+ end
74
+
75
+ def send_method(object, method, args)
76
+ object.send method, *case arity = self.arity(object, method)
77
+ when 0; []
78
+ when -1; [name].concat(args)
79
+ else; [name].concat(args).slice(0..arity - 1)
80
+ end
81
+ end
82
+
83
+ def arity(object, method)
84
+ object.class.instance_method(method).arity rescue 0
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,45 @@
1
+ module SimpleStates
2
+ class States < Module
3
+ class << self
4
+ def init(object)
5
+ object.singleton_class.send(:include, proxy_for(object.class))
6
+ if object.singleton_class.respond_to?(:after_initialize)
7
+ object.singleton_class.after_initialize { self.state = self.class.initial_state if state.blank? }
8
+ else
9
+ object.state = object.class.initial_state
10
+ end
11
+ end
12
+
13
+ def proxy_for(klass)
14
+ klass.const_defined?(:States, false) ? klass::States : klass.const_set(:States, new(klass.events))
15
+ end
16
+ end
17
+
18
+ def initialize(events)
19
+ merge_events(events).each do |event|
20
+ define_event(event)
21
+ end
22
+ end
23
+
24
+ protected
25
+
26
+ def define_event(event)
27
+ define_method(event.name) do |*args|
28
+ event.send(:call, self, *args) do
29
+ super(*args) if self.class.method_defined?(event.name)
30
+ end
31
+ end
32
+
33
+ define_method(:"#{event.name}!") do |*args|
34
+ event.saving do
35
+ send(event.name, *args)
36
+ end
37
+ end
38
+ end
39
+
40
+ def merge_events(events)
41
+ merges, events = *events.partition { |event| event.name == :all }
42
+ events.each { |event| merges.each { |merge| event.merge(merge) } }
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,3 @@
1
+ module SimpleStates
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simple_states
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sven Fuchs
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-08-04 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: &70306211479800 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70306211479800
25
+ - !ruby/object:Gem::Dependency
26
+ name: hashr
27
+ requirement: &70306211479060 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 0.0.10
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70306211479060
36
+ - !ruby/object:Gem::Dependency
37
+ name: test_declarative
38
+ requirement: &70306211478360 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70306211478360
47
+ - !ruby/object:Gem::Dependency
48
+ name: mocha
49
+ requirement: &70306211477140 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70306211477140
58
+ description: ! '[description]'
59
+ email: svenfuchs@artweb-design.de
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - lib/simple_states/event.rb
65
+ - lib/simple_states/states.rb
66
+ - lib/simple_states/version.rb
67
+ - lib/simple_states.rb
68
+ - Gemfile
69
+ - Gemfile.lock
70
+ - LICENSE
71
+ - NOTES.md
72
+ - Rakefile
73
+ - README.md
74
+ homepage: https://github.com/svenfuchs/simple_states
75
+ licenses: []
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project: ! '[none]'
94
+ rubygems_version: 1.8.6
95
+ signing_key:
96
+ specification_version: 3
97
+ summary: ! '[summary]'
98
+ test_files: []