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,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zenaton
4
+ # :nodoc
5
+ module Refinements
6
+ refine Struct do
7
+ def to_zenaton
8
+ class_name = self.class.name.to_s
9
+ error_message = 'Only named structs are supported'
10
+ raise ArgumentError, error_message if class_name.empty?
11
+ {
12
+ 'v' => values
13
+ }
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ # Reimplements `json/add/struct`
20
+ class Struct
21
+ def self.from_zenaton(props)
22
+ new(*props['v'])
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zenaton
4
+ # :nodoc
5
+ module Refinements
6
+ refine Symbol do
7
+ def to_zenaton
8
+ {
9
+ 's' => to_s
10
+ }
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ # Reimplements `json/add/symbol`
17
+ class Symbol
18
+ def self.from_zenaton(props)
19
+ props['s'].to_sym
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zenaton
4
+ # :nodoc
5
+ module Refinements
6
+ refine Time do
7
+ def to_zenaton
8
+ nanoseconds = [tv_usec * 1000]
9
+ respond_to?(:tv_nsec) && nanoseconds << tv_nsec
10
+ nanoseconds = nanoseconds.max
11
+ {
12
+ 's' => tv_sec,
13
+ 'n' => nanoseconds
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ # Reimplements `json/add/time`
21
+ class Time
22
+ def self.from_zenaton(props)
23
+ if (usec = props.delete('u')) # used to be tv_usec -> tv_nsec
24
+ props['n'] = usec * 1000
25
+ end
26
+ if method_defined?(:tv_nsec)
27
+ at(props['s'], Rational(props['n'], 1000))
28
+ else
29
+ at(props['s'], props['n'] / 1000)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zenaton/exceptions'
4
+
5
+ module Zenaton
6
+ # Collection of utility classes for the Zenaton library
7
+ module Services
8
+ # Service that:
9
+ # - handles graphql calls
10
+ # - translates exceptions into Zenaton specific ones
11
+ class GraphQL
12
+ CREATE_WORKFLOW_SCHEDULE = <<-'GRAPHQL'
13
+ mutation ($createWorkflowScheduleInput: CreateWorkflowScheduleInput!) {
14
+ createWorkflowSchedule(input: $createWorkflowScheduleInput) {
15
+ schedule {
16
+ id
17
+ }
18
+ }
19
+ }
20
+ GRAPHQL
21
+
22
+ CREATE_TASK_SCHEDULE = <<-'GRAPHQL'
23
+ mutation ($createTaskScheduleInput: CreateTaskScheduleInput!) {
24
+ createTaskSchedule(input: $createTaskScheduleInput) {
25
+ schedule {
26
+ id
27
+ }
28
+ }
29
+ }
30
+ GRAPHQL
31
+
32
+ def initialize(http:)
33
+ @http = http
34
+ end
35
+
36
+ # Makes a GRAPHQL request with some data and sets the correct headers
37
+ #
38
+ # @param url [String] the url for the request
39
+ # @param body [Hash] the payload to send with the request
40
+ # @return [Hash] the parsed json response
41
+ def request(url, query, variables = nil, headers = {})
42
+ body = { 'query' => query }
43
+ body['variables'] = variables if variables
44
+
45
+ res_body = @http.post(url, body, headers)
46
+ handle_response_body(res_body)
47
+ end
48
+
49
+ private
50
+
51
+ def handle_response_body(response_body)
52
+ if external_error?(response_body)
53
+ raise Zenaton::ExternalError, format_external_error(response_body)
54
+ end
55
+
56
+ response_body && response_body['data']
57
+ end
58
+
59
+ def external_error?(response_body)
60
+ response_body&.key?('errors')
61
+ end
62
+
63
+ def format_external_error(response_body)
64
+ response_body['errors'].map do |error|
65
+ path = error['path'] ? "- #{error['path']}: " : ''
66
+ "#{path}#{error['message']}"
67
+ end.join("\n")
68
+ end
69
+ end
70
+ end
71
+ end
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
3
4
  require 'net/http'
4
5
  require 'zenaton/exceptions'
6
+ require 'json'
5
7
 
6
8
  module Zenaton
7
9
  # Collection of utility classes for the Zenaton library
@@ -37,11 +39,12 @@ module Zenaton
37
39
  #
38
40
  # @param url [String] the url for the request
39
41
  # @param body [Hash] the payload to send with the request
42
+ # @param headers [Hash] additional headers to send with the request
40
43
  # @return [Hash] the parsed json response
41
- def post(url, body)
44
+ def post(url, body, headers = {})
42
45
  parsed_url = parse_url(url)
43
46
  request = Net::HTTP::Post.new(parsed_url[:uri])
44
- set_body_and_headers(request, post_options(body))
47
+ set_body_and_headers(request, post_options(body, headers))
45
48
  make_request(request, parsed_url)
46
49
  end
47
50
 
@@ -49,11 +52,12 @@ module Zenaton
49
52
  #
50
53
  # @param url [String] the url for the request
51
54
  # @param body [Hash] the payload to send with the request
55
+ # @param headers [Hash] additional headers to send with the request
52
56
  # @return [Hash] the parsed json response
53
- def put(url, body)
57
+ def put(url, body, headers = {})
54
58
  parsed_url = parse_url(url)
55
59
  request = Net::HTTP::Put.new(parsed_url[:uri])
56
- set_body_and_headers(request, put_options(body))
60
+ set_body_and_headers(request, put_options(body, headers))
57
61
  make_request(request, parsed_url)
58
62
  end
59
63
 
@@ -104,13 +108,14 @@ module Zenaton
104
108
  }
105
109
  end
106
110
 
107
- def post_options(body)
111
+ def post_options(body, headers)
112
+ default_post_headers = {
113
+ 'Accept' => 'application/json',
114
+ 'Content-Type' => 'application/json'
115
+ }
108
116
  {
109
117
  body: body,
110
- headers: {
111
- 'Accept' => 'application/json',
112
- 'Content-Type' => 'application/json'
113
- }
118
+ headers: default_post_headers.merge(headers)
114
119
  }
115
120
  end
116
121
  alias put_options post_options
@@ -1,37 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'singleton'
4
- require 'json/add/date'
5
- require 'json/add/date_time'
6
- require 'json/add/exception'
7
- require 'json/add/range'
8
- require 'json/add/regexp'
9
- require 'json/add/struct'
10
- require 'json/add/time'
11
- require 'json/add/rational'
12
- require 'json/add/complex'
13
- require 'json/add/bigdecimal'
14
- require 'json/add/ostruct'
4
+ require 'zenaton/refinements'
15
5
 
16
6
  module Zenaton
17
7
  module Services
18
8
  # Wrapper class to read instance variables from an object and
19
9
  # to create new objects with a given set of instance variables.
20
10
  class Properties
21
- # Handle (de)serializaton separately for these classes.
22
- SPECIAL_CASES = [
23
- ::Complex,
24
- ::Date,
25
- ::DateTime,
26
- ::Range,
27
- ::Rational,
28
- ::Regexp,
29
- ::Symbol,
30
- ::Time,
31
- defined?(::OpenStruct) ? ::OpenStruct : nil,
32
- defined?(::BigDecimal) ? ::BigDecimal : nil
33
- ].compact.freeze
34
-
11
+ using Zenaton::Refinements
35
12
  # Handle blank object instantiation differently for these classes
36
13
  NUMERIC_INITIALIATION = [
37
14
  ::Rational,
@@ -59,11 +36,7 @@ module Zenaton
59
36
  # @param object [Object] the object to be read
60
37
  # @return [Hash]
61
38
  def from(object)
62
- return from_complex_type(object) if special_case?(object)
63
- object.instance_variables.map do |ivar|
64
- value = object.instance_variable_get(ivar)
65
- [ivar, value]
66
- end.to_h
39
+ object.to_zenaton
67
40
  end
68
41
 
69
42
  # Returns the given object with the properties as instance variables
@@ -71,11 +44,10 @@ module Zenaton
71
44
  # @param properties [Hash] the properties to be written
72
45
  # @return [Object]
73
46
  def set(object, properties)
74
- return set_complex_type(object, properties) if special_case?(object)
75
- properties.each do |ivar, value|
76
- object.instance_variable_set(ivar, value)
77
- end
78
- object
47
+ klass = object.class
48
+ return klass.from_zenaton(properties) if defined?(klass.from_zenaton)
49
+
50
+ fallback_set(object, properties)
79
51
  end
80
52
 
81
53
  # Given a class name and a set of properties, return a new instance of the
@@ -102,29 +74,11 @@ module Zenaton
102
74
  super_class.nil? || object.is_a?(super_class)
103
75
  end
104
76
 
105
- def from_complex_type(object)
106
- if object.is_a?(Symbol)
107
- { 's' => object.to_s }
108
- else
109
- JSON.parse(object.to_json).tap do |attributes|
110
- attributes.delete('json_class')
111
- end
112
- end
113
- end
114
-
115
- def set_complex_type(object, props)
116
- if object.is_a?(Symbol)
117
- props['s'].to_sym
118
- else
119
- props['json_class'] = object.class.name
120
- JSON(props.to_json)
77
+ def fallback_set(object, properties)
78
+ properties.each do |ivar, value|
79
+ object.instance_variable_set(ivar, value)
121
80
  end
122
- end
123
-
124
- def special_case?(object)
125
- SPECIAL_CASES.include?(object.class) \
126
- || object.is_a?(Struct) \
127
- || object.is_a?(Exception)
81
+ object
128
82
  end
129
83
  end
130
84
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
3
4
  require 'zenaton/services/properties'
5
+ require 'json'
4
6
 
5
7
  module Zenaton
6
8
  module Services
@@ -69,8 +69,10 @@ module Zenaton
69
69
  def _weekday(value, day, now, now_dup)
70
70
  _set_mode(MODE_WEEK_DAY)
71
71
  value -= 1 if later_today?(now, day)
72
- value.times { |_n| now_dup = now_dup.next_occurring(day) }
73
- now_dup
72
+ current_day_number = now.wday != 0 ? now.wday - 1 : 6
73
+ from_now = WEEKDAYS.index(day) - current_day_number
74
+ from_now += 7 * value unless from_now.positive?
75
+ now_dup.advance(days: from_now)
74
76
  end
75
77
 
76
78
  def _timestamp(timestamp)
@@ -22,6 +22,15 @@ module Zenaton
22
22
  Engine.instance.dispatch([self])
23
23
  end
24
24
 
25
+ def schedule(cron)
26
+ if !cron.is_a?(String) || cron.blank?
27
+ raise InvalidArgumentError,
28
+ "The cron passed to 'schedule' must be a non empty string"
29
+ end
30
+
31
+ Engine.instance.schedule([self], cron)
32
+ end
33
+
25
34
  class_methods do
26
35
  # Search for workflows to interact with.
27
36
  # For available methods, see {Zenaton::Query::Builder}
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Zenaton
4
4
  # This gem's current version
5
- VERSION = '0.4.2'
5
+ VERSION = '0.5.0'
6
6
  end
@@ -25,10 +25,12 @@ Gem::Specification.new do |spec|
25
25
  spec.add_runtime_dependency 'activesupport'
26
26
  spec.add_runtime_dependency 'tzinfo-data'
27
27
 
28
+ spec.add_development_dependency 'appraisal'
28
29
  spec.add_development_dependency 'bundler', '~> 1.16'
29
30
  spec.add_development_dependency 'pry'
30
31
  spec.add_development_dependency 'rake', '~> 10.0'
31
32
  spec.add_development_dependency 'rspec', '~> 3.0'
33
+ spec.add_development_dependency 'rspec_junit_formatter'
32
34
  spec.add_development_dependency 'rubocop'
33
35
  spec.add_development_dependency 'rubocop-rspec'
34
36
  spec.add_development_dependency 'simplecov'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zenaton
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zenaton
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-08-05 00:00:00.000000000 Z
11
+ date: 2019-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: appraisal
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +108,20 @@ dependencies:
94
108
  - - "~>"
95
109
  - !ruby/object:Gem::Version
96
110
  version: '3.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec_junit_formatter
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
97
125
  - !ruby/object:Gem::Dependency
98
126
  name: rubocop
99
127
  requirement: !ruby/object:Gem::Requirement
@@ -190,6 +218,7 @@ files:
190
218
  - ".rspec"
191
219
  - ".rubocop.yml"
192
220
  - ".yardopts"
221
+ - Appraisals
193
222
  - CHANGELOG.md
194
223
  - CODE_OF_CONDUCT.md
195
224
  - Gemfile
@@ -199,8 +228,15 @@ files:
199
228
  - Rakefile
200
229
  - bin/console
201
230
  - bin/setup
231
+ - gemfiles/.bundle/config
232
+ - gemfiles/rails_5.0.gemfile
233
+ - gemfiles/rails_5.1.gemfile
234
+ - gemfiles/rails_5.2.gemfile
202
235
  - lib/zenaton.rb
203
236
  - lib/zenaton/client.rb
237
+ - lib/zenaton/contexts.rb
238
+ - lib/zenaton/contexts/task.rb
239
+ - lib/zenaton/contexts/workflow.rb
204
240
  - lib/zenaton/engine.rb
205
241
  - lib/zenaton/exceptions.rb
206
242
  - lib/zenaton/interfaces/event.rb
@@ -210,6 +246,22 @@ files:
210
246
  - lib/zenaton/parallel.rb
211
247
  - lib/zenaton/processor.rb
212
248
  - lib/zenaton/query/builder.rb
249
+ - lib/zenaton/refinements.rb
250
+ - lib/zenaton/refinements/big_decimal.rb
251
+ - lib/zenaton/refinements/class.rb
252
+ - lib/zenaton/refinements/complex.rb
253
+ - lib/zenaton/refinements/date.rb
254
+ - lib/zenaton/refinements/date_time.rb
255
+ - lib/zenaton/refinements/exception.rb
256
+ - lib/zenaton/refinements/object.rb
257
+ - lib/zenaton/refinements/open_struct.rb
258
+ - lib/zenaton/refinements/range.rb
259
+ - lib/zenaton/refinements/rational.rb
260
+ - lib/zenaton/refinements/regexp.rb
261
+ - lib/zenaton/refinements/struct.rb
262
+ - lib/zenaton/refinements/symbol.rb
263
+ - lib/zenaton/refinements/time.rb
264
+ - lib/zenaton/services/graphql.rb
213
265
  - lib/zenaton/services/http.rb
214
266
  - lib/zenaton/services/properties.rb
215
267
  - lib/zenaton/services/serializer.rb