argon 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7833b8b1180240aca7e95882353b3661de98af55
4
- data.tar.gz: aa01804bbf4cdf5d19ea2b20817f1cc9f7f1feda
3
+ metadata.gz: d09b17a786ef14a57f421fc3a44d18a3422e57ff
4
+ data.tar.gz: 3389b1b936db32747285bf13d990cbc829e4af7a
5
5
  SHA512:
6
- metadata.gz: 556b6d45c27c1812f1a4bc8999c505bf16dad8a1771eac57002abae7ccadecd22a9d3d8a45b8b9bc01bbfdb1dd7167f3939d90f6194ff6a7f36bbb4ec569476d
7
- data.tar.gz: 5642eda6be0759bb1521603a62b5106fa95cc5c34bf5bfc8f2f7f0769906c3e14ce91288bd16ec145fa6410b8d2c3428ec2bc38e0264d27006e997e0ee19c917
6
+ metadata.gz: 439e99b9f44cfdb6964925572d649bd2e8458daddc9db818f8990ea94ec1316e40a6917b2f75995c0670e0551cae58eb3cee30a299e90e08d60d390dd3acdad8
7
+ data.tar.gz: 446e84e3808c6f89e778f02178f249f840386c45c7d57e899cdf1334669a03062b2d570ddecc50d7c386a71a3a2cd3a76cd9a7729d2da1e806cb52b260251fa8
data/.gitignore CHANGED
@@ -10,3 +10,4 @@
10
10
 
11
11
  # rspec failure tracking
12
12
  .rspec_status
13
+ *.gem
data/README.md CHANGED
@@ -1,15 +1,13 @@
1
- # Maxim
1
+ # Argon
2
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/maxim`. 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
3
+ Argon is a workflow engine for Rails, built around a state machine. It relies on a `state` propery on a model to manage workflow around state transitions.
6
4
 
7
5
  ## Installation
8
6
 
9
7
  Add this line to your application's Gemfile:
10
8
 
11
9
  ```ruby
12
- gem 'maxim'
10
+ gem 'argon'
13
11
  ```
14
12
 
15
13
  And then execute:
@@ -18,11 +16,63 @@ And then execute:
18
16
 
19
17
  Or install it yourself as:
20
18
 
21
- $ gem install maxim
19
+ $ gem install argon
22
20
 
23
21
  ## Usage
24
22
 
25
- TODO: Write usage instructions here
23
+ The `Maxim` module provides a `state_machine` class method which expects the following args (as demonstrated by the example below).
24
+
25
+ ```ruby
26
+ class Report
27
+ include Argon
28
+
29
+ def on_cancel
30
+ # This is called from inside the lock, after the edge transitions. If an exception is thrown here, the entire transition is rolled back
31
+ end
32
+
33
+ def after_cancel
34
+ # This is called after a successful transition
35
+ end
36
+
37
+ state_machine state: {
38
+ states: {
39
+ draft: 0,
40
+ submitted: 1,
41
+ approved: 2,
42
+ rejected: 3,
43
+ cancelled: 4,
44
+ },
45
+ events: [
46
+ :cancel,
47
+ ],
48
+ edges: [
49
+ { from: :draft, to: :submitted, action: :submit, callbacks: {in: false, post: false} },
50
+ { from: :draft, to: :cancelled, action: :cancel_draft, callbacks: {in: false, post: false}, on_events: [:cancel] },
51
+ { from: :submitted, to: :cancelled, action: :cancel_submitted, callbacks: {in: false, post: false}, on_events: [:cancel] },
52
+ ],
53
+ on_successful_transition: ->(from:, to:) { /* Do something here */ },
54
+ on_failed_transition: ->(from:, to:) { /* Do something else */ },
55
+ }
56
+ end
57
+ ```
58
+
59
+ `Report#state` will now return one of `:draft`, `:submitted`, `:approved`, `:rejected` or `:cancelled`. There is no method to set the state directly, and it is recommended to set the numberical value of the initial state (e.g. `0` here for `:draft`) as the default value of the `state` column for the `reports` table, which should be an integer column.
60
+
61
+ This will generate the following methods for the states:
62
+
63
+ * `Report.draft`, `Report.submitted`, `Report.approved`, `Report.rejected`, and `Report.cancelled` : These are scopes similar to the ones generated by Rails enum
64
+ * `Report#draft?`, `Report#submitted?`, `Report#approved?`, `Report#rejected?`, and `Report#cancelled?` : These are query methods similar to the ones generated by Rails enum
65
+
66
+ The following methods are generated from the edges:
67
+
68
+ * `Report#submit!` : This will move the state to `submitted` if the object was in the `draft` state. The state change is done inside a lock (`ActiveRecord::Locking::Pessimistic#with_lock`). If `callbacks.in` was true, then `Report#on_submit` is called from within the lock. If `callbacks.post` was true, then `Report#after_submit` will be called, after the lock is released. Note that if enabled, the callbacks have to be defined before `state_machine` is called, or an exception will be raised.
69
+ * `Report#can_submit?` : This will return `true` if the object was in the `draft` state.
70
+
71
+ (similar methods are created for `cancel_draft` and `cancel_submitted`)
72
+
73
+ The following method will be generated for the event:
74
+
75
+ * `Report#cancel!` : This will check `can_cancel_draft?`, and if true, will call `cancel_draft!`. Else, it will try `can_cancel_submitted?`, and call `cancel_submitted!` if true, and so on. Note that for event methods, both callbacks are mandatory (i.e. here, both `on_cancel` and `after_cancel` must be defined before `state_machine` is called).
26
76
 
27
77
  ## Development
28
78
 
@@ -32,7 +82,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
32
82
 
33
83
  ## Contributing
34
84
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/maxim. 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.
85
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sparkymat/argon. 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
86
 
37
87
  ## License
38
88
 
@@ -40,4 +90,4 @@ The gem is available as open source under the terms of the [MIT License](http://
40
90
 
41
91
  ## Code of Conduct
42
92
 
43
- Everyone interacting in the Maxim project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/maxim/blob/master/CODE_OF_CONDUCT.md).
93
+ Everyone interacting in the Argon project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/sparkymat/argon/blob/master/CODE_OF_CONDUCT.md).
data/lib/argon/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Argon
2
- VERSION = "1.0.1"
2
+ VERSION = "1.0.2"
3
3
  end
data/lib/argon.rb CHANGED
@@ -32,11 +32,11 @@ module Argon
32
32
  raise Argon::Error.new("`events` should be an Array of Symbols") if !events_list.is_a?(Array) || (events_list.length > 0 && events_list.map(&:class).uniq != [Symbol])
33
33
  events_list.each do |event_name|
34
34
  raise Argon::Error.new("`#{event_name}` is not a valid event name. `#{self.name}##{event_name}` method already exists") if self.instance_methods.include?(event_name)
35
- raise Argon::Error.new("`on_#{event_name}(from:, to:, context:)` not found") if !self.instance_methods.include?("on_#{event_name}".to_sym) || self.instance_method("on_#{event_name}".to_sym).parameters.to_set != [[:keyreq, :from],[:keyreq, :to],[:keyreq, :context]].to_set
36
- raise Argon::Error.new("`after_#{event_name}(from:, to:, context:)` not found") if !self.instance_methods.include?("after_#{event_name}".to_sym) || self.instance_method("after_#{event_name}".to_sym).parameters.to_set != [[:keyreq, :from],[:keyreq, :to],[:keyreq, :context]].to_set
35
+ raise Argon::Error.new("`on_#{event_name}()` not found") if !self.instance_methods.include?("on_#{event_name}".to_sym) || self.instance_method("on_#{event_name}".to_sym).parameters.to_set != [].to_set
36
+ raise Argon::Error.new("`after_#{event_name}()` not found") if !self.instance_methods.include?("after_#{event_name}".to_sym) || self.instance_method("after_#{event_name}".to_sym).parameters.to_set != [].to_set
37
37
  end
38
38
 
39
- raise Argon::Error.new("`edges` should be an Array of Hashes, with keys: from, to, action, callbacks{in: true/false, post: true/false}, on_events (optional)") if !edges_list.is_a?(Array) || edges_list.map(&:class).uniq != [Hash]
39
+ raise Argon::Error.new("`edges` should be an Array of Hashes, with keys: from, to, action, callbacks{on: true/false, after: true/false}, on_events (optional)") if !edges_list.is_a?(Array) || edges_list.map(&:class).uniq != [Hash]
40
40
 
41
41
  registered_edge_pairs = [].to_set
42
42
  edges_list.each_with_index do |edge_details, index|
@@ -46,13 +46,19 @@ module Argon
46
46
  do_action = "#{action}!".to_sym
47
47
  check_action = "can_#{action}?".to_sym
48
48
 
49
- raise Argon::Error.new("`edges` should be an Array of Hashes, with keys: from, to, action, callbacks{in: true/false, post: true/false}, on_events (optional)") unless edge_details.keys.to_set.subset?([:from, :to, :action, :callbacks, :on_events].to_set) && [:from, :to, :action, :callbacks].to_set.subset?(edge_details.keys.to_set)
49
+ raise Argon::Error.new("`edges` should be an Array of Hashes, with keys: from, to, action, callbacks{on: true/false, after: true/false}, on_events (optional)") unless edge_details.keys.to_set.subset?([:from, :to, :action, :callbacks, :on_events].to_set) && [:from, :to, :action, :callbacks].to_set.subset?(edge_details.keys.to_set)
50
50
  raise Argon::Error.new("`edges[#{index}].from` is not a valid state") unless states_map.keys.include?(from)
51
51
  raise Argon::Error.new("`edges[#{index}].to` is not a valid state") unless states_map.keys.include?(to)
52
52
  raise Argon::Error.new("`edges[#{index}].action` is not a Symbol") unless action.is_a?(Symbol)
53
53
  raise Argon::Error.new("`#{edge_details[:action]}` is an invalid action name. `#{self.name}##{do_action}` method already exists") if self.instance_methods.include?(do_action)
54
54
  raise Argon::Error.new("`#{edge_details[:action]}` is an invalid action name. `#{self.name}##{check_action}` method already exists") if self.instance_methods.include?(check_action)
55
- raise Argon::Error.new("`edges[#{index}].callbacks` must be {in: true/false, post: true/false}") if !edge_details[:callbacks].is_a?(Hash) || edge_details[:callbacks].keys.to_set != [:post, :in].to_set || !edge_details[:callbacks].values.to_set.subset?([true, false].to_set)
55
+ raise Argon::Error.new("`edges[#{index}].callbacks` must be {on: true/false, after: true/false}") if !edge_details[:callbacks].is_a?(Hash) || edge_details[:callbacks].keys.to_set != [:after, :on].to_set || !edge_details[:callbacks].values.to_set.subset?([true, false].to_set)
56
+ if edge_details[:callbacks][:on]
57
+ raise Argon::Error.new("`on_#{edge_details[:action]}()` not found") if !self.instance_methods.include?("on_#{edge_details[:action]}".to_sym) || self.instance_method("on_#{edge_details[:action]}".to_sym).parameters.to_set != [].to_set
58
+ end
59
+ if edge_details[:callbacks][:after]
60
+ raise Argon::Error.new("`after_#{edge_details[:action]}()` not found") if !self.instance_methods.include?("after_#{edge_details[:action]}".to_sym) || self.instance_method("after_#{edge_details[:action]}".to_sym).parameters.to_set != [].to_set
61
+ end
56
62
  raise Argon::Error.new("`#{edge_details[:on_events]}` (`edges[#{index}].on_events`) is not a valid list of events") if !edge_details[:on_events].nil? && !edge_details[:on_events].is_a?(Array)
57
63
  unless edge_details[:on_events].nil?
58
64
  edge_details[:on_events].each_with_index do |event_name, event_index|
@@ -63,11 +69,11 @@ module Argon
63
69
  registered_edge_pairs << [from, to]
64
70
  end
65
71
 
66
- raise Argon::Error.new("`on_successful_transition` must be a lambda of signature `(from:, to:, context:)`") if !on_successful_transition.nil? && !on_successful_transition.is_a?(Proc)
67
- raise Argon::Error.new("`on_successful_transition` must be a lambda of signature `(from:, to:, context:)`") if on_successful_transition.parameters.to_set != [[:keyreq, :from],[:keyreq, :to]].to_set && on_successful_transition.parameters.to_set != [[:keyreq, :from],[:keyreq, :to],[:keyreq, :context]].to_set
72
+ raise Argon::Error.new("`on_successful_transition` must be a lambda of signature `(from:, to:)`") if !on_successful_transition.nil? && !on_successful_transition.is_a?(Proc)
73
+ raise Argon::Error.new("`on_successful_transition` must be a lambda of signature `(from:, to:)`") if on_successful_transition.parameters.to_set != [[:keyreq, :from],[:keyreq, :to]].to_set
68
74
 
69
- raise Argon::Error.new("`on_failed_transition` must be a lambda of signature `(from:, to:, context:)`") if !on_failed_transition.nil? && !on_failed_transition.is_a?(Proc)
70
- raise Argon::Error.new("`on_failed_transition` must be a lambda of signature `(from:, to:, context:)`") if on_failed_transition.parameters.to_set != [[:keyreq, :from],[:keyreq, :to]].to_set && on_failed_transition.parameters.to_set != [[:keyreq, :from],[:keyreq, :to],[:keyreq, :context]].to_set
75
+ raise Argon::Error.new("`on_failed_transition` must be a lambda of signature `(from:, to:)`") if !on_failed_transition.nil? && !on_failed_transition.is_a?(Proc)
76
+ raise Argon::Error.new("`on_failed_transition` must be a lambda of signature `(from:, to:)`") if on_failed_transition.parameters.to_set != [[:keyreq, :from],[:keyreq, :to]].to_set
71
77
 
72
78
  state_machines = {}
73
79
  begin
@@ -109,16 +115,16 @@ module Argon
109
115
  from = edge_details[:from]
110
116
  to = edge_details[:to]
111
117
  action = edge_details[:action]
112
- in_lock_callback = "on_#{action}".to_sym if edge_details[:callbacks][:in] == true
113
- post_lock_callback = "after_#{action}".to_sym if edge_details[:callbacks][:post] == true
118
+ on_lock_callback = "on_#{action}".to_sym if edge_details[:callbacks][:on] == true
119
+ after_lock_callback = "after_#{action}".to_sym if edge_details[:callbacks][:after] == true
114
120
 
115
121
  define_method("can_#{action}?".to_sym) do
116
122
  self.send(field) == from
117
123
  end
118
124
 
119
- define_method("#{action}!".to_sym) do |context = nil, &block|
125
+ define_method("#{action}!".to_sym) do |&block|
120
126
  if self.send(field) != from
121
- on_failed_transition.call(from: self.send(field), to: to, context: context)
127
+ on_failed_transition.call(from: self.send(field), to: to)
122
128
  raise Argon::InvalidTransitionError.new("Invalid state transition")
123
129
  end
124
130
 
@@ -127,8 +133,8 @@ module Argon
127
133
  self.update_column(field, self.class.send("#{ field.to_s.pluralize }").map{|v| [v[0],v[1]]}.to_h[to])
128
134
  self.touch
129
135
 
130
- unless in_lock_callback.nil?
131
- self.send(in_lock_callback, from: from, to: to, context: context)
136
+ unless on_lock_callback.nil?
137
+ self.send(on_lock_callback)
132
138
  end
133
139
 
134
140
  unless block.nil?
@@ -136,32 +142,30 @@ module Argon
136
142
  end
137
143
  end
138
144
  rescue => e
139
- on_failed_transition.call(from: self.send(field), to: to, context: context)
145
+ on_failed_transition.call(from: self.send(field), to: to)
140
146
  raise e
141
147
  end
142
148
 
143
- on_successful_transition.call(from: from, to: to, context: context)
149
+ on_successful_transition.call(from: from, to: to)
144
150
 
145
- unless post_lock_callback.nil?
146
- self.send(post_lock_callback, from: from, to: to, context: context)
151
+ unless after_lock_callback.nil?
152
+ self.send(after_lock_callback)
147
153
  end
148
154
  end
149
155
  end
150
156
 
151
157
  events_list.each do |event_name|
152
- define_method("#{event_name}!".to_sym) do |context = nil|
158
+ define_method("#{event_name}!".to_sym) do
153
159
  matching_edges = edges_list.select{ |edge| !edge[:on_events].nil? && edge[:on_events].to_set.include?(event_name) }
154
160
 
155
161
  matching_edges.each do |edge|
156
162
  action = edge[:action]
157
- from = edge[:from]
158
- to = edge[:to]
159
163
 
160
164
  if self.send("can_#{ action }?")
161
- self.send("#{ action }!", context) do
162
- self.send("on_#{ event_name }", from: from, to: to, context: context)
165
+ self.send("#{ action }!") do
166
+ self.send("on_#{ event_name }")
163
167
  end
164
- self.send("after_#{ event_name }", from: from, to: to, context: context)
168
+ self.send("after_#{ event_name }")
165
169
  return
166
170
  end
167
171
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: argon
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ajith Hussain
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-10-05 00:00:00.000000000 Z
11
+ date: 2017-10-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler