zenaton 0.4.2 → 0.5.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +79 -52
  3. data/.gitignore +7 -0
  4. data/.rubocop.yml +4 -0
  5. data/Appraisals +13 -0
  6. data/CHANGELOG.md +17 -1
  7. data/Gemfile.lock +17 -8
  8. data/gemfiles/.bundle/config +2 -0
  9. data/gemfiles/rails_5.0.gemfile +7 -0
  10. data/gemfiles/rails_5.1.gemfile +7 -0
  11. data/gemfiles/rails_5.2.gemfile +7 -0
  12. data/lib/zenaton/client.rb +63 -0
  13. data/lib/zenaton/contexts.rb +7 -0
  14. data/lib/zenaton/contexts/task.rb +33 -0
  15. data/lib/zenaton/contexts/workflow.rb +17 -0
  16. data/lib/zenaton/engine.rb +16 -0
  17. data/lib/zenaton/interfaces/task.rb +30 -1
  18. data/lib/zenaton/interfaces/workflow.rb +15 -0
  19. data/lib/zenaton/refinements.rb +16 -0
  20. data/lib/zenaton/refinements/big_decimal.rb +25 -0
  21. data/lib/zenaton/refinements/class.rb +21 -0
  22. data/lib/zenaton/refinements/complex.rb +22 -0
  23. data/lib/zenaton/refinements/date.rb +26 -0
  24. data/lib/zenaton/refinements/date_time.rb +40 -0
  25. data/lib/zenaton/refinements/exception.rb +24 -0
  26. data/lib/zenaton/refinements/object.rb +15 -0
  27. data/lib/zenaton/refinements/open_struct.rb +26 -0
  28. data/lib/zenaton/refinements/range.rb +21 -0
  29. data/lib/zenaton/refinements/rational.rb +22 -0
  30. data/lib/zenaton/refinements/regexp.rb +22 -0
  31. data/lib/zenaton/refinements/struct.rb +24 -0
  32. data/lib/zenaton/refinements/symbol.rb +21 -0
  33. data/lib/zenaton/refinements/time.rb +32 -0
  34. data/lib/zenaton/services/graphql.rb +71 -0
  35. data/lib/zenaton/services/http.rb +14 -9
  36. data/lib/zenaton/services/properties.rb +11 -57
  37. data/lib/zenaton/services/serializer.rb +2 -0
  38. data/lib/zenaton/traits/with_timestamp.rb +4 -2
  39. data/lib/zenaton/traits/zenatonable.rb +9 -0
  40. data/lib/zenaton/version.rb +1 -1
  41. data/zenaton.gemspec +2 -0
  42. metadata +54 -2
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zenaton
4
+ # Represents the current runtime context of a Workflow or Task.
5
+ module Contexts
6
+ end
7
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zenaton
4
+ module Contexts
5
+ # Represents the current runtime context of a Task.
6
+ #
7
+ # The information provided by the context can be useful to alter the
8
+ # behaviour of the task.
9
+ #
10
+ # For example, you can use the attempt index to know if a task has been
11
+ # automatically retried or not and how many times, and decide to do
12
+ # something when you did not expect the task to be retried more than X
13
+ # times.
14
+ #
15
+ # You can also use the attempt number in the `on_error_retry_delay` method
16
+ # of a task in order to implement complex retry strategies.
17
+ class Task
18
+ # @return [String] The UUID identifying the current task
19
+ attr_reader :id
20
+
21
+ # @return [Integer] The number of times this task has been automatically
22
+ # retried. This counter is reset if you issue a manual retry from your
23
+ # dashboard
24
+ attr_reader :retry_index
25
+
26
+ # @return [Zenaton::Contexts::Task] a new execution context for a task
27
+ def initialize(**kwargs)
28
+ @id = kwargs[:id]
29
+ @retry_index = kwargs[:retry_index]
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zenaton
4
+ module Contexts
5
+ # Represents the current runtime context of a Workflow.
6
+ class Workflow
7
+ # @return [String] The UUID identifying the current workflow
8
+ attr_reader :id
9
+
10
+ # @return [Zenaton::Contexts::Workflow] a new execution context for a
11
+ # workflow
12
+ def initialize(**kwargs)
13
+ @id = kwargs[:id]
14
+ end
15
+ end
16
+ end
17
+ end
@@ -24,6 +24,22 @@ module Zenaton
24
24
  @processor = nil
25
25
  end
26
26
 
27
+ # Executes scheduling jobs synchronously
28
+ # @param jobs [Array<Zenaton::Interfaces::Job>]
29
+ # @param cron String
30
+ # @return [Array<String>, nil] the results if executed locally, or nil
31
+ def schedule(jobs, cron)
32
+ jobs.map(&method(:check_argument))
33
+ jobs.map do |job|
34
+ if job.is_a? Interfaces::Workflow
35
+ @client.start_scheduled_workflow(job, cron)
36
+ else
37
+ @client.start_scheduled_task(job, cron)
38
+ end
39
+ end
40
+ nil
41
+ end
42
+
27
43
  # Executes jobs synchronously
28
44
  # @param jobs [Array<Zenaton::Interfaces::Job>]
29
45
  # @return [Array<String>, nil] the results if executed locally, or nil
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'zenaton/interfaces/job'
4
+ require 'zenaton/contexts/task'
4
5
 
5
6
  module Zenaton
6
7
  module Interfaces
@@ -9,7 +10,35 @@ module Zenaton
9
10
  # Child classes should implement the handle method
10
11
  def handle
11
12
  raise NotImplemented,
12
- "Your workflow does not implement the `handle' method"
13
+ "Your workflow does not implement the `#handle' method"
14
+ end
15
+
16
+ # (Optional) Implement this method for automatic retrial of task in
17
+ # case of failures.
18
+ # @param _exception [Exception] the reason for the task failure.
19
+ # @return [Integer, FalseClass, NilClass] the non-negative amount of
20
+ # seconds to wait before automatically retrying this task. Falsy values
21
+ # will avoid retrial. Other values will cause the retrial to fail.
22
+ def on_error_retry_delay(_exception)
23
+ nil
24
+ end
25
+
26
+ # @return [Zenaton::Contexts::Task] the task execution context
27
+ def context
28
+ @context || Contexts::Task.new
29
+ end
30
+
31
+ # @private
32
+ # Sets a new context if none has been set yet.
33
+ # This is called from the zenaton agent and will raise if called twice.
34
+ # @raise [ArgumentError] when the context was already set.
35
+ def add_context(**attributes)
36
+ message = <<~ERROR
37
+ Context has already been set and cannot be mutated.
38
+ ERROR
39
+ raise ArgumentError, message if @context
40
+
41
+ @context = Contexts::Task.new(attributes)
13
42
  end
14
43
  end
15
44
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'zenaton/interfaces/job'
4
+ require 'zenaton/contexts/workflow'
4
5
 
5
6
  module Zenaton
6
7
  module Interfaces
@@ -18,6 +19,20 @@ module Zenaton
18
19
  def id
19
20
  nil
20
21
  end
22
+
23
+ # @return [Zenaton::Contexts::Workflow] the workflow execution context
24
+ def context
25
+ @context || Contexts::Workflow.new
26
+ end
27
+
28
+ # @private
29
+ # Sets a new context if none has been set yet.
30
+ # This is called from the zenaton agent and will raise if called twice.
31
+ # @raise [ArgumentError] when the context was already set.
32
+ def add_context(**attributes)
33
+ raise ArgumentError if @context
34
+ @context = Contexts::Workflow.new(attributes)
35
+ end
21
36
  end
22
37
  end
23
38
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zenaton/refinements/big_decimal'
4
+ require 'zenaton/refinements/class'
5
+ require 'zenaton/refinements/complex'
6
+ require 'zenaton/refinements/date'
7
+ require 'zenaton/refinements/date_time'
8
+ require 'zenaton/refinements/exception'
9
+ require 'zenaton/refinements/object'
10
+ require 'zenaton/refinements/open_struct'
11
+ require 'zenaton/refinements/range'
12
+ require 'zenaton/refinements/rational'
13
+ require 'zenaton/refinements/regexp'
14
+ require 'zenaton/refinements/struct'
15
+ require 'zenaton/refinements/symbol'
16
+ require 'zenaton/refinements/time'
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/AndOr
4
+ defined?(::BigDecimal) or require 'bigdecimal'
5
+ # rubocop:enable Style/AndOr
6
+
7
+ module Zenaton
8
+ # :nodoc
9
+ module Refinements
10
+ refine BigDecimal do
11
+ def to_zenaton
12
+ {
13
+ 'b' => _dump
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ # Reimplements `json/add/bigdecimal`
21
+ class BigDecimal
22
+ def self.from_zenaton(props)
23
+ BigDecimal._load props['b']
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zenaton
4
+ # :nodoc
5
+ module Refinements
6
+ refine Class do
7
+ def to_zenaton
8
+ {
9
+ 'n' => name
10
+ }
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ # Load an instance of class from zenaton properties
17
+ class Class
18
+ def self.from_zenaton(props)
19
+ Object.const_get(props['n'])
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zenaton
4
+ # :nodoc
5
+ module Refinements
6
+ refine Complex do
7
+ def to_zenaton
8
+ {
9
+ 'r' => real,
10
+ 'i' => imag
11
+ }
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ # Reimplements `json/add/complex`
18
+ class Complex
19
+ def self.from_zenaton(props)
20
+ Complex(props['r'], props['i'])
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+
5
+ module Zenaton
6
+ # :nodoc
7
+ module Refinements
8
+ refine Date do
9
+ def to_zenaton
10
+ {
11
+ 'y' => year,
12
+ 'm' => month,
13
+ 'd' => day,
14
+ 'sg' => start
15
+ }
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ # Reimplements `json/add/date`
22
+ class Date
23
+ def self.from_zenaton(props)
24
+ civil(*props.values_at('y', 'm', 'd', 'sg'))
25
+ end
26
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+
5
+ module Zenaton
6
+ # :nodoc
7
+ module Refinements
8
+ refine DateTime do
9
+ def to_zenaton
10
+ {
11
+ 'y' => year,
12
+ 'm' => month,
13
+ 'd' => day,
14
+ 'H' => hour,
15
+ 'M' => min,
16
+ 'S' => sec,
17
+ 'of' => offset.to_s,
18
+ 'sg' => start
19
+ }
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ # Reimplements `json/add/date_time`
26
+ class DateTime
27
+ def self.from_zenaton(props)
28
+ args = props.values_at('y', 'm', 'd', 'H', 'M', 'S')
29
+ of_a, of_b = props['of'].split('/')
30
+ # rubocop:disable Style/ConditionalAssignment
31
+ if of_b && of_b != '0'
32
+ args << Rational(of_a.to_i, of_b.to_i)
33
+ else
34
+ args << of_a
35
+ end
36
+ # rubocop:enable Style/ConditionalAssignment
37
+ args << props['sg']
38
+ civil(*args)
39
+ end
40
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zenaton
4
+ # :nodoc
5
+ module Refinements
6
+ refine Exception do
7
+ def to_zenaton
8
+ {
9
+ 'm' => message,
10
+ 'b' => backtrace
11
+ }
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ # Reimplements `json/add/exception`
18
+ class Exception
19
+ def self.from_zenaton(props)
20
+ result = new(props['m'])
21
+ result.set_backtrace props['b']
22
+ result
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zenaton
4
+ # :nodoc
5
+ module Refinements
6
+ refine Object do
7
+ def to_zenaton
8
+ instance_variables.map do |ivar|
9
+ value = instance_variable_get(ivar)
10
+ [ivar, value]
11
+ end.to_h
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ostruct'
4
+
5
+ module Zenaton
6
+ # :nodoc
7
+ module Refinements
8
+ refine OpenStruct do
9
+ def to_zenaton
10
+ class_name = self.class.name.to_s
11
+ error_message = 'Only named structs are supported'
12
+ raise ArgumentError, error_message if class_name.empty?
13
+ {
14
+ 't' => table
15
+ }
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ # Reimplements `json/add/ostruct`
22
+ class OpenStruct
23
+ def self.from_zenaton(props)
24
+ new(props['t'] || props[:t])
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zenaton
4
+ # :nodoc
5
+ module Refinements
6
+ refine Range do
7
+ def to_zenaton
8
+ {
9
+ 'a' => [first, last, exclude_end?]
10
+ }
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ # Reimplements `json/add/range`
17
+ class Range
18
+ def self.from_zenaton(props)
19
+ new(*props['a'])
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zenaton
4
+ # :nodoc
5
+ module Refinements
6
+ refine Rational do
7
+ def to_zenaton
8
+ {
9
+ 'n' => numerator,
10
+ 'd' => denominator
11
+ }
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ # Reimplements `json/add/rational`
18
+ class Rational
19
+ def self.from_zenaton(props)
20
+ Rational(props['n'], props['d'])
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zenaton
4
+ # :nodoc
5
+ module Refinements
6
+ refine Regexp do
7
+ def to_zenaton
8
+ {
9
+ 'o' => options,
10
+ 's' => source
11
+ }
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ # Reimplements `json/add/regexp`
18
+ class Regexp
19
+ def self.from_zenaton(props)
20
+ new(props['s'], props['o'])
21
+ end
22
+ end