oflow 1.0.2 → 1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5d34d97bd5ac308c65b62516297d16c06b41fa0e091bcb21a395a9b7564b023b
4
+ data.tar.gz: bd541cf3747b7d10a82fa297148475b6e52173eef8fc5ed455bb1d124c70f26a
5
+ SHA512:
6
+ metadata.gz: 795edf243b99e83917dd6382c7874357eef78ab73419f36aa32b0fba943c6416418d5b7afc4641d8e89612dedaa8b8fc1757fbb51e5b07345cf590c3c15e05e6
7
+ data.tar.gz: 9c0566dec233c2ed6465db6a79196369578636e2d7aacbe48fc61e2a002dc361697cb68f4494fd67d426dbd97a20c0bf539e6152a4739ed86820af69219f46c2
data/README.md CHANGED
@@ -25,6 +25,10 @@ Follow [@peterohler on Twitter](http://twitter.com/#!/peterohler) for announceme
25
25
 
26
26
  ## Release Notes
27
27
 
28
+ ### Release 1.1.0
29
+
30
+ - Changed the gemchart example to use a new Recorder actor.
31
+
28
32
  ### Release 1.0.2
29
33
 
30
34
  - Updated GemChart sample to handle poor connections better.
@@ -13,6 +13,7 @@ require 'oflow/actors/balancer'
13
13
  require 'oflow/actors/trigger'
14
14
  require 'oflow/actors/merger'
15
15
  require 'oflow/actors/persister'
16
+ require 'oflow/actors/recorder'
16
17
  require 'oflow/actors/timer'
17
18
  require 'oflow/actors/httpserver'
18
19
  require 'oflow/actors/shellone'
@@ -45,9 +45,9 @@ module OFlow
45
45
  continue if :success == key || 'success' == key
46
46
  begin
47
47
  task.ship(key, box)
48
- rescue BlockedError => e
48
+ rescue BlockedError
49
49
  task.warn("Failed to ship timer #{box.contents} to #{key}. Task blocked.")
50
- rescue BusyError => e
50
+ rescue BusyError
51
51
  task.warn("Failed to ship timer #{box.contents} to #{key}. Task busy.")
52
52
  end
53
53
  end
@@ -0,0 +1,184 @@
1
+ require 'oj'
2
+
3
+ module OFlow
4
+ module Actors
5
+
6
+ # Actor that saves records to the local file system as JSON
7
+ # representations of the records as lines in a single file associated with
8
+ # one of the elements of the JSON record. The message that triggers the
9
+ # store must have a 'table' element, a 'key', and a 'rec' element.
10
+ class Recorder < Actor
11
+
12
+ attr_reader :dir
13
+
14
+ # Initializes the recorder with options of:
15
+ # @param [Hash] options with keys of
16
+ # - :dir [String] directory to store the persisted records
17
+ # - :results_path [String] path to where the results should be placed in
18
+ # the request (default: nil or ship only results)
19
+ def initialize(task, options)
20
+ super
21
+ @cache = {}
22
+ @dir = options[:dir]
23
+ if @dir.nil?
24
+ @dir = File.join('db', task.full_name.gsub(':', '/'))
25
+ end
26
+ @dir = File.expand_path(@dir.strip)
27
+ @results_path = options[:results_path]
28
+ @results_path.strip! unless @results_path.nil?
29
+
30
+ if Dir.exist?(@dir)
31
+ Dir.glob(File.join(@dir, '*.json')).each do |path|
32
+ load(path)
33
+ end
34
+ else
35
+ `mkdir -p #{@dir}`
36
+ end
37
+ end
38
+
39
+ def perform(op, box)
40
+ dest = box.contents[:dest]
41
+ result = nil
42
+ case op
43
+ when :insert, :create
44
+ result = insert(box)
45
+ when :get, :read
46
+ result = read(box)
47
+ when :update
48
+ result = update(box)
49
+ when :insert_update
50
+ result = insert_update(box)
51
+ when :delete, :remove
52
+ result = delete(box)
53
+ when :query
54
+ result = query(box)
55
+ when :clear
56
+ result = clear(box)
57
+ else
58
+ raise OpError.new(task.full_name, op)
59
+ end
60
+ unless dest.nil?
61
+ if @results_path.nil?
62
+ box = Box.new(result, box.tracker)
63
+ else
64
+ box = box.set(@results_path, result)
65
+ end
66
+ task.ship(dest, box)
67
+ end
68
+ end
69
+
70
+ def insert(box)
71
+ table = box.get('table')
72
+ key = box.get('key')
73
+ rec = box.get('rec')
74
+ raise KeyError.new(:insert) if table.nil?
75
+ raise KeyError.new(:insert) if key.nil?
76
+
77
+ tc = @cache[table]
78
+ if tc.nil?
79
+ tc = {}
80
+ @cache[table] = tc
81
+ end
82
+ tc[key] = rec
83
+ write(table)
84
+ end
85
+
86
+ alias :update :insert
87
+ alias :insert_update :insert
88
+
89
+ def read(box)
90
+ table = box.get('table')
91
+ key = box.get('key')
92
+ raise KeyError.new(:read) if table.nil?
93
+ raise KeyError.new(:read) if key.nil?
94
+
95
+ tc = @cache[table]
96
+ return nil if tc.nil?
97
+
98
+ rec = tc[key]
99
+ rec
100
+ end
101
+
102
+ def delete(box)
103
+ table = box.get('table')
104
+ key = box.get('key')
105
+ raise KeyError.new(:read) if table.nil?
106
+ raise KeyError.new(:read) if key.nil?
107
+
108
+ tc = @cache[table]
109
+ unless tc.nil?
110
+ tc.delete(key)
111
+ write(table)
112
+ end
113
+ nil
114
+ end
115
+
116
+ def query(box)
117
+ recs = {}
118
+ expr = box.get('expr')
119
+ table = box.get('table')
120
+ raise KeyError.new(:query) if table.nil?
121
+
122
+ tc = @cache[table]
123
+ tc.each do |key,rec|
124
+ recs[key] = rec if (expr.nil? || expr.call(rec, key))
125
+ end
126
+ recs
127
+ end
128
+
129
+ def clear(box)
130
+ @cache = {}
131
+ `rm -rf #{@dir}`
132
+ # remake the dir in preparation for future inserts
133
+ `mkdir -p #{@dir}`
134
+ nil
135
+ end
136
+
137
+ private
138
+
139
+ def write(table)
140
+ filename = "#{table}.json"
141
+ path = File.join(@dir, filename)
142
+ Oj.to_file(path, @cache[table], :mode => :strict)
143
+ end
144
+
145
+ def load(path)
146
+ return nil unless File.exist?(path)
147
+ tc = Oj.load_file(path, :mode => :strict, symbol_keys: true)
148
+ name = File.basename(path)[0..-File.extname(path).size - 1]
149
+ @cache[name] = tc
150
+ end
151
+
152
+ class TableError < Exception
153
+ def initialize(table)
154
+ super("No Table found for #{table}")
155
+ end
156
+ end # TableError
157
+
158
+ class KeyError < Exception
159
+ def initialize(key)
160
+ super("No key found for #{key}")
161
+ end
162
+ end # KeyError
163
+
164
+ class SeqError < Exception
165
+ def initialize(op, key)
166
+ super("No sequence number found for #{op} of #{key}")
167
+ end
168
+ end # SeqError
169
+
170
+ class ExistsError < Exception
171
+ def initialize(key, seq)
172
+ super("#{key}:#{seq} already exists")
173
+ end
174
+ end # ExistsError
175
+
176
+ class NotFoundError < Exception
177
+ def initialize(key)
178
+ super("#{key} not found")
179
+ end
180
+ end # NotFoundError
181
+
182
+ end # Recorder
183
+ end # Actors
184
+ end # OFlow
@@ -12,10 +12,11 @@ module OFlow
12
12
 
13
13
  # When to trigger the first event. nil means start now.
14
14
  attr_reader :start
15
- # The stop time. If nil then there is not stopping unless the repeat limit
16
- # kicks in.
15
+ # The stop time. If nil then there is no stopping unless the repeat
16
+ # limit kicks in.
17
17
  attr_reader :stop
18
- # How long to wait between each trigger. nil indicates as fast as possible,
18
+ # How long to wait between each trigger. nil indicates as fast as
19
+ # possible,
19
20
  attr_reader :period
20
21
  # How many time to repeat before stopping. nil mean go forever.
21
22
  attr_reader :repeat
@@ -23,11 +24,13 @@ module OFlow
23
24
  attr_reader :label
24
25
  # The number of time the timer has fired or shipped.
25
26
  attr_reader :count
26
- # Boolean flag indicating a tracker should be added to the trigger content
27
- # if true.
27
+ # Boolean flag indicating a tracker should be added to the trigger
28
+ # content if true.
28
29
  attr_reader :with_tracker
29
30
  # Time of next or pending trigger.
30
31
  attr_reader :pending
32
+ # Flag indicating a trigger should be fired on start as well.
33
+ attr_reader :on_start
31
34
 
32
35
  def initialize(task, options={})
33
36
  @count = 0
@@ -35,6 +38,7 @@ module OFlow
35
38
  @stop = nil
36
39
  @period = nil
37
40
  @repeat = nil
41
+ @on_start = false
38
42
  set_options(options)
39
43
  @pending = @start
40
44
  super
@@ -70,6 +74,9 @@ module OFlow
70
74
  when :with_tracker
71
75
  set_with_tracker(box.nil? ? nil : box.contents)
72
76
  end
77
+ if @on_start
78
+ trigger(Time.now())
79
+ end
73
80
  while true
74
81
  now = Time.now()
75
82
  # If past stop time then it is done. A future change in options can
@@ -87,22 +94,14 @@ module OFlow
87
94
  unless Task::STOPPED == task.state
88
95
  @count += 1
89
96
  now = Time.now()
90
- tracker = @with_tracker ? Tracker.create(@label) : nil
91
- box = Box.new([@label, @count, now.utc()], tracker)
92
- task.links.each_key do |key|
93
- begin
94
- task.ship(key, box)
95
- rescue BlockedError => e
96
- task.warn("Failed to ship timer #{box.contents} to #{key}. Task blocked.")
97
- rescue BusyError => e
98
- task.warn("Failed to ship timer #{box.contents} to #{key}. Task busy.")
99
- end
100
- end
97
+ trigger(now)
101
98
  end
102
99
  if @period.nil? || @period == 0
103
100
  @pending = now
104
101
  else
105
- @pending += period
102
+ diff = now - @pending
103
+ @pending += @period * diff.to_i/@period.to_i
104
+ @pending += @period if @pending <= now
106
105
  end
107
106
  end
108
107
  # If there is a request waiting then return so it can be handled. It
@@ -122,12 +121,27 @@ module OFlow
122
121
  end
123
122
  end
124
123
 
124
+ def trigger(now)
125
+ tracker = @with_tracker ? Tracker.create(@label) : nil
126
+ box = Box.new([@label, @count, now.utc()], tracker)
127
+ task.links.each_key do |key|
128
+ begin
129
+ task.ship(key, box)
130
+ rescue BlockedError
131
+ task.warn("Failed to ship timer #{box.contents} to #{key}. Task blocked.")
132
+ rescue BusyError
133
+ task.warn("Failed to ship timer #{box.contents} to #{key}. Task busy.")
134
+ end
135
+ end
136
+ end
137
+
125
138
  def set_options(options)
126
139
  set_start(options[:start]) # if nil let start get set to now
127
140
  set_stop(options[:stop]) if options.has_key?(:stop)
128
141
  set_period(options[:period]) if options.has_key?(:period)
129
142
  set_repeat(options[:repeat]) if options.has_key?(:repeat)
130
143
  set_with_tracker(options[:with_tracker])
144
+ set_on_start(options[:on_start])
131
145
  @label = options[:label].to_s
132
146
  end
133
147
 
@@ -135,7 +149,7 @@ module OFlow
135
149
  if v.is_a?(String)
136
150
  begin
137
151
  v = DateTime.parse(v).to_time
138
- v = v - v.gmtoff
152
+ v = v - v.localtime.gmtoff
139
153
  rescue Exception
140
154
  v = Time.now() + v.to_i
141
155
  end
@@ -193,6 +207,18 @@ module OFlow
193
207
  @label = v
194
208
  end
195
209
 
210
+ def set_on_start(v)
211
+ if v.is_a?(TrueClass)
212
+ @on_start = true
213
+ elsif v.is_a?(FalseClass)
214
+ @on_start = false
215
+ elsif v.nil?
216
+ # no change
217
+ else
218
+ @on_start = ('true' == v.to_s.strip.downcase)
219
+ end
220
+ end
221
+
196
222
  def set_with_tracker(v)
197
223
  v = false if v.nil?
198
224
  unless true == v || false == v
@@ -200,7 +226,7 @@ module OFlow
200
226
  end
201
227
  @with_tracker = v
202
228
  end
203
-
229
+
204
230
  end # Timer
205
231
  end # Actors
206
232
  end # OFlow
@@ -20,8 +20,12 @@ module OFlow
20
20
  @name = File.basename(filename, '.graffle')
21
21
 
22
22
  nodes = doc.locate('plist/dict')[0].nodes
23
- nodes = Graffle.get_key_value(nodes, 'GraphicsList')
24
-
23
+ sheets = Graffle.get_key_value(nodes, 'Sheets')
24
+ if sheets.nil?
25
+ nodes = Graffle.get_key_value(nodes, 'GraphicsList')
26
+ else
27
+ nodes = Graffle.get_key_value(sheets[0].nodes, 'GraphicsList')
28
+ end
25
29
  raise ConfigError.new("Empty flow.") if nodes.nil?
26
30
 
27
31
  nodes.each do |node|
@@ -1,5 +1,5 @@
1
1
 
2
2
  module OFlow
3
3
  # Current version of the module.
4
- VERSION = '1.0.2'
4
+ VERSION = '1.1.0'
5
5
  end
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
5
- prerelease:
4
+ version: 1.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Peter Ohler
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2016-06-26 00:00:00.000000000 Z
11
+ date: 2018-06-21 00:00:00.000000000 Z
13
12
  dependencies: []
14
13
  description: Operations Workflow in Ruby. This implements a workflow/process flow
15
14
  using multiple task nodes that each have their own queues and execution thread.
@@ -19,7 +18,11 @@ extensions: []
19
18
  extra_rdoc_files:
20
19
  - README.md
21
20
  files:
21
+ - LICENSE
22
+ - README.md
23
+ - lib/oflow.rb
22
24
  - lib/oflow/actor.rb
25
+ - lib/oflow/actors.rb
23
26
  - lib/oflow/actors/balancer.rb
24
27
  - lib/oflow/actors/errorhandler.rb
25
28
  - lib/oflow/actors/httpserver.rb
@@ -27,12 +30,12 @@ files:
27
30
  - lib/oflow/actors/log.rb
28
31
  - lib/oflow/actors/merger.rb
29
32
  - lib/oflow/actors/persister.rb
33
+ - lib/oflow/actors/recorder.rb
30
34
  - lib/oflow/actors/relay.rb
31
35
  - lib/oflow/actors/shellone.rb
32
36
  - lib/oflow/actors/shellrepeat.rb
33
37
  - lib/oflow/actors/timer.rb
34
38
  - lib/oflow/actors/trigger.rb
35
- - lib/oflow/actors.rb
36
39
  - lib/oflow/box.rb
37
40
  - lib/oflow/env.rb
38
41
  - lib/oflow/errors.rb
@@ -45,12 +48,11 @@ files:
45
48
  - lib/oflow/pattern.rb
46
49
  - lib/oflow/stamp.rb
47
50
  - lib/oflow/task.rb
51
+ - lib/oflow/test.rb
48
52
  - lib/oflow/test/action.rb
49
53
  - lib/oflow/test/actorwrap.rb
50
- - lib/oflow/test.rb
51
54
  - lib/oflow/tracker.rb
52
55
  - lib/oflow/version.rb
53
- - lib/oflow.rb
54
56
  - test/actors/bad.rb
55
57
  - test/actors/balancer_test.rb
56
58
  - test/actors/doubler.rb
@@ -77,33 +79,30 @@ files:
77
79
  - test/stutter.rb
78
80
  - test/task_test.rb
79
81
  - test/tracker_test.rb
80
- - LICENSE
81
- - README.md
82
82
  homepage: http://www.ohler.com/oflow
83
83
  licenses:
84
84
  - MIT
85
+ metadata: {}
85
86
  post_install_message:
86
87
  rdoc_options:
87
- - --main
88
+ - "--main"
88
89
  - README.md
89
90
  require_paths:
90
91
  - lib
91
92
  required_ruby_version: !ruby/object:Gem::Requirement
92
- none: false
93
93
  requirements:
94
- - - ! '>='
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  required_rubygems_version: !ruby/object:Gem::Requirement
98
- none: false
99
98
  requirements:
100
- - - ! '>='
99
+ - - ">="
101
100
  - !ruby/object:Gem::Version
102
101
  version: '0'
103
102
  requirements: []
104
103
  rubyforge_project: oflow
105
- rubygems_version: 1.8.23.2
104
+ rubygems_version: 2.7.6
106
105
  signing_key:
107
- specification_version: 3
106
+ specification_version: 4
108
107
  summary: Operations Workflow in Ruby
109
108
  test_files: []