zenaton 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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