ex_machina 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 65774af491192a74ddda0683c63af42d4de122a1
4
+ data.tar.gz: 8e22de0cc6d23b9d20dc755e3dbab298efd2a9e4
5
+ SHA512:
6
+ metadata.gz: e50f7e90615c99ef72b6bddfecffdfcfc4df135d8f550ff0656092e1398eec8fc975c725a7110204c19e7294c134be5724273d26c2ba3c5c11c0c54932bce341
7
+ data.tar.gz: 77e6132fe89cb046378a284e77147529fbf5114c71a99e3f07a3a9e3b6b51e4c646c290023fd5a30b44c7ba9dd216f1757a22fa7ed62c6426e746f923f6f7083
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Rodrigo Panachi
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/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # ExMachina
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/ex_machina`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'ex_machina'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install ex_machina
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/ex_machina. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
41
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ex_machina"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ex_machina/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ex_machina"
8
+ spec.version = ExMachina::VERSION
9
+ spec.authors = ["Rodrigo Panachi"]
10
+ spec.email = ["rpanachi@gmail.com"]
11
+
12
+ spec.summary = %q{A simple and OOP implementation of StateMachine}
13
+ spec.homepage = "https://github.com/rpanachi/ex_machina"
14
+ spec.license = "MIT"
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.bindir = "exe"
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.11"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "rspec", "~> 3.0"
23
+ end
@@ -0,0 +1,139 @@
1
+ module ExMachina
2
+ module Event
3
+ class Runner
4
+
5
+ STATUSES = [
6
+ SUCCESS = :success,
7
+ FAILURE = :failure,
8
+ ERROR = :error,
9
+ SKIPPED = :skipped
10
+ ].freeze
11
+
12
+ attr_accessor :previous, :current
13
+ attr_accessor :result
14
+
15
+ attr_reader :event, :transition
16
+
17
+ def initialize(event, transition)
18
+ @event = event
19
+ @transition = transition
20
+ end
21
+
22
+ def running?
23
+ @running
24
+ end
25
+
26
+ def start!
27
+ @run = false
28
+ @running = true
29
+ @result = nil
30
+ @previous = event.status
31
+ @current = nil
32
+ end
33
+
34
+ def finish!(value)
35
+ return result unless running?
36
+
37
+ @running = false
38
+ @ran = true
39
+
40
+ if value == true
41
+ success!
42
+ elsif value == false
43
+ failure!
44
+ elsif value == :skipped
45
+ skipped!
46
+ elsif value == :error
47
+ error!
48
+ else
49
+ @result = value
50
+ end
51
+ end
52
+ def run?
53
+ !!@ran
54
+ end
55
+
56
+ def success?
57
+ result == :success
58
+ end
59
+ def success!
60
+ @current = transition.to
61
+ finish!(@result = :success)
62
+ end
63
+
64
+ def failure?
65
+ result == :failure
66
+ end
67
+ def failure!
68
+ @current = @previous
69
+ finish!(@result = :failure)
70
+ end
71
+
72
+ def skipped!
73
+ finish!(@result = :skipped)
74
+ end
75
+ def skipped?
76
+ result == :skipped
77
+ end
78
+
79
+ def error!(exception)
80
+ # TODO handle exception
81
+ finish!(@result = :error)
82
+ end
83
+ def error?
84
+ result == :error
85
+ end
86
+
87
+ def eligible?
88
+ if transition.conditional?
89
+ invoke(event, transition.do_if, self) ||
90
+ invoke(event, transition.do_unless, self)
91
+ else
92
+ true
93
+ end
94
+ end
95
+
96
+ def run
97
+ start!
98
+
99
+ return skipped! unless eligible?
100
+
101
+ invoke(event, transition.do_before, self)
102
+ begin
103
+ call = invoke(event, :call, self)
104
+ invoke(event, transition.do_after, self)
105
+ finish!(call)
106
+ rescue StandardError => ex
107
+ error!(ex)
108
+ end
109
+
110
+ if success?
111
+ invoke(event, :change_status, self)
112
+ invoke(event, transition.do_success, self)
113
+ elsif failure?
114
+ invoke(event, transition.do_failure, self)
115
+ end
116
+
117
+ call
118
+ end
119
+
120
+ def invoke(context, meth, *args)
121
+ return if meth.nil?
122
+
123
+ if meth.respond_to?(:call)
124
+ all_args = Array([context, *args])
125
+ meth_params = meth.parameters
126
+ meth_args = *all_args.first(meth_params.size)
127
+
128
+ meth.call(*meth_args)
129
+
130
+ elsif context.respond_to?(meth)
131
+ meth_params = context.method(meth).parameters
132
+ meth_args = *args.first(meth_params.size)
133
+
134
+ context.send(meth, *meth_args)
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,42 @@
1
+ module ExMachina
2
+ module Event
3
+ class Transition
4
+
5
+ attr_reader :from, :to
6
+ attr_reader :do_if, :do_unless, :do_before, :do_after, :do_success, :do_failure
7
+
8
+ attr_accessor :previous, :current, :context, :result
9
+
10
+ def initialize(options)
11
+ @from = Array(options.fetch(:from)).map { |status| normalize(status) }
12
+ @to = normalize(options.fetch(:to))
13
+
14
+ # optional args
15
+ @do_if = options[:if]
16
+ @do_unless = options[:unless]
17
+ @do_before = options[:before]
18
+ @do_after = options[:after]
19
+ @do_success = options[:success]
20
+ @do_failure = options[:failure]
21
+ end
22
+
23
+ def from?(status)
24
+ from.include?(normalize(status))
25
+ end
26
+
27
+ def to?(status)
28
+ Array(to).include?(normalize(status))
29
+ end
30
+
31
+ def conditional?
32
+ !do_if.nil? || !do_unless.nil?
33
+ end
34
+
35
+ private
36
+
37
+ def normalize(status)
38
+ status.to_s.to_sym
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,22 @@
1
+ module ExMachina
2
+ module Event
3
+ module Validations
4
+ def errors
5
+ @errors ||= []
6
+ end
7
+ def valid?
8
+ errors.empty?
9
+ end
10
+ def validate
11
+ errors.clear
12
+
13
+ unless transitions.any?
14
+ errors << "No transitions defined from '#{status}' status"
15
+ end
16
+ end
17
+ def error_messages
18
+ errors.join(", ")
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,92 @@
1
+ require "ex_machina/event/transition"
2
+ require "ex_machina/event/runner"
3
+ require "ex_machina/event/validations"
4
+
5
+ module ExMachina
6
+ module Event
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ base.include(InstanceMethods)
10
+ base.include(Validations)
11
+ end
12
+
13
+ module ClassMethods
14
+ def transitions
15
+ @transitions ||= []
16
+ end
17
+ def transition(*args)
18
+ transitions << Transition.new(*args)
19
+ end
20
+ def fire(context)
21
+ self.new(context).fire
22
+ end
23
+ def fire!(context)
24
+ self.new(context).fire!
25
+ end
26
+ def can_fire?(context)
27
+ self.new(context).can_fire?
28
+ end
29
+ def event
30
+ self.name.demodulize.underscore
31
+ end
32
+ end
33
+
34
+ module InstanceMethods
35
+ attr_reader :context
36
+
37
+ def initialize(context)
38
+ @context = context
39
+ end
40
+
41
+ def transitions
42
+ self.class
43
+ .transitions
44
+ .select { |transition| transition.from?(status) }
45
+ end
46
+
47
+ def status
48
+ context.status
49
+ end
50
+
51
+ def change_status(execution)
52
+ context.status = execution.current.to_s
53
+ context.save
54
+ end
55
+
56
+ def fire
57
+ validate
58
+ return unless can_fire?
59
+
60
+ result = false
61
+ within_transaction do
62
+ transitions.each do |transition|
63
+ runner = Runner.new(self, transition)
64
+ result = runner.run
65
+
66
+ break result unless runner.skipped?
67
+ end
68
+ end
69
+ result
70
+ end
71
+
72
+ def fire!
73
+ raise error_messages unless can_fire?
74
+ fire
75
+ end
76
+
77
+ def can_fire?
78
+ validate
79
+ valid?
80
+ end
81
+
82
+ def call
83
+ raise NotImplementedError, "method 'call' must be implemented on event class"
84
+ end
85
+
86
+ def within_transaction(&block)
87
+ # TODO implement strategy for transaction
88
+ DB.transaction(&block)
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,33 @@
1
+ module ExMachina
2
+ module Eventable
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ base.include InstanceMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def events
10
+ @events ||= []
11
+ end
12
+ def has_events(*event_classes)
13
+ event_classes.each do |event_class|
14
+ events << event_class
15
+
16
+ define_method "#{event_class.event}" do
17
+ event_class.fire(self)
18
+ end
19
+ define_method "#{event_class.event}!" do
20
+ event_class.fire!(self)
21
+ end
22
+ define_method "can_#{event_class.event}?" do
23
+ event_class.can_fire?(self)
24
+ end
25
+ events << event_class
26
+ end
27
+ end
28
+ end
29
+
30
+ module InstanceMethods
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module ExMachina
2
+ VERSION = "0.0.1"
3
+ end
data/lib/ex_machina.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "ex_machina/version"
2
+ require "ex_machina/event"
3
+ require "ex_machina/eventable"
4
+
5
+ module ExMachina
6
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ex_machina
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Rodrigo Panachi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-03-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description:
56
+ email:
57
+ - rpanachi@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - bin/console
69
+ - bin/setup
70
+ - ex_machina.gemspec
71
+ - lib/ex_machina.rb
72
+ - lib/ex_machina/event.rb
73
+ - lib/ex_machina/event/runner.rb
74
+ - lib/ex_machina/event/transition.rb
75
+ - lib/ex_machina/event/validations.rb
76
+ - lib/ex_machina/eventable.rb
77
+ - lib/ex_machina/version.rb
78
+ homepage: https://github.com/rpanachi/ex_machina
79
+ licenses:
80
+ - MIT
81
+ metadata: {}
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 2.5.1
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: A simple and OOP implementation of StateMachine
102
+ test_files: []