cucumber-messages 0.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fae43985a14dd510cdad40de4e15f44d41a25d2cc5872eb8ce95b486995ddf9e
4
+ data.tar.gz: 3bd925db18e60f98b718ebd017e328dcc2938c8022b526bf4d3e057ae1ecb39a
5
+ SHA512:
6
+ metadata.gz: 120be88738c8374cd69723b9e4b2a33664f8f802cf72af92a46b6dfef12c9520b85c0e853861102bad57948f5f019d7cff50e5565833e91b2a136e7f3ae7a30d
7
+ data.tar.gz: ba5ccba6194a142270aadb05e5db76728a1dc57f1fe1617fd25d593f069761b4da0c95b06030da83051b7e932d2bd2e1d3050a144c44dbae5fef5ada0a4fae4d
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Cucumber Messages for Ruby (JSON schema)
2
+
3
+ See main [README](../README.md)
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1,24 @@
1
+ require 'securerandom'
2
+
3
+ module Cucumber
4
+ module Messages
5
+ module IdGenerator
6
+ class Incrementing
7
+ def initialize
8
+ @index = -1
9
+ end
10
+
11
+ def new_id
12
+ @index += 1
13
+ @index.to_s
14
+ end
15
+ end
16
+
17
+ class UUID
18
+ def new_id
19
+ SecureRandom.uuid
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,37 @@
1
+ require 'cucumber/messages/message/utils'
2
+ require 'json'
3
+
4
+ module Cucumber
5
+ module Messages
6
+ class Message
7
+ include Cucumber::Messages::Message::Utils
8
+
9
+ module Deserialization
10
+ def self.included(other)
11
+ other.extend(ClassMethods)
12
+ end
13
+
14
+ module ClassMethods
15
+ ##
16
+ # Returns a new Message - or messages into an array - deserialized from the given json document.
17
+ # CamelCased keys are properly converted to snake_cased attributes in the process
18
+ #
19
+ # Cucumber::Messages::Duration.from_json('{"seconds":1,"nanos":42}') # => #<Cucumber::Messages::Duration:0x00007efda134c290 @seconds=1, @nanos=42>
20
+ # Cucumber::Messages::PickleTag.from_json('{"name":"foo","astNodeId":"abc-def"}') # => #<Cucumber::Messages::PickleTag:0x00007efda138cdb8 @name="foo", @ast_node_id="abc-def">
21
+ #
22
+ # It is recursive so embedded messages are also processed.
23
+ #
24
+ # json_string = { location: { line: 2 }, text: "comment" }.to_json
25
+ # Cucumber::Messages::Comment.from_json(json_string) # => #<Cucumber::Messages::Comment:0x00007efda6abf888 @location=#<Cucumber::Messages::Location:0x00007efda6abf978 @line=2, @column=nil>, @text="comment">
26
+ #
27
+ # json_string = { uri: 'file:///...', comments: [{text: 'text comment'}, {text: 'another comment'}]}.to_json
28
+ # Cucumber::Messages::GherkinDocument.from_json(json_string) # => #<Cucumber::Messages::GherkinDocument:0x00007efda11e6a90 ... @comments=[#<Cucumber::Messages::Comment:0x00007efda11e6e50 ..., #<Cucumber::Messages::Comment:0x00007efda11e6b58 ...>]>
29
+ ##
30
+ def from_json(json_string)
31
+ from_h(JSON.parse(json_string, { symbolize_names: true }))
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,70 @@
1
+ require 'cucumber/messages/message/utils'
2
+ require 'json'
3
+
4
+ module Cucumber
5
+ module Messages
6
+ class Message
7
+ include Cucumber::Messages::Message::Utils
8
+
9
+ module Serialization
10
+ ##
11
+ # Returns a new Hash formed from the message attributes
12
+ # If +camelize:+ keyword parameter is set to true, then keys will be camelized
13
+ # If +reject_nil_values:+ keyword parameter is set to true, resulting hash won't include nil values
14
+ #
15
+ # Cucumber::Messages::Duration.new(seconds: 1, nanos: 42).to_h # => { seconds: 1, nanos: 42 }
16
+ # Cucumber::Messages::PickleTag.new(name: 'foo', ast_node_id: 'abc-def').to_h(camelize: true) # => { name: 'foo', astNodeId: 'abc-def' }
17
+ # Cucumber::Messages::PickleTag.new(name: 'foo', ast_node_id: nil).to_h(reject_nil_values: true) # => { name: 'foo' }
18
+ #
19
+ # It is recursive so embedded messages are also processed
20
+ #
21
+ # location = Cucumber::Messages::Location.new(line: 2)
22
+ # Cucumber::Messages::Comment.new(location: location, text: 'comment').to_h # => { location: { line: 2, :column: nil }, text: "comment" }
23
+ ##
24
+ def to_h(camelize: false, reject_nil_values: false)
25
+ resulting_hash = self.instance_variables.map do |variable_name|
26
+ h_key = variable_name[1..-1]
27
+ h_key = Cucumber::Messages::Message.camelize(h_key) if camelize
28
+
29
+ h_value = prepare_value(
30
+ self.instance_variable_get(variable_name),
31
+ camelize: camelize,
32
+ reject_nil_values: reject_nil_values
33
+ )
34
+
35
+ [ h_key.to_sym, h_value ]
36
+ end.to_h
37
+
38
+ resulting_hash.reject! { |_, value| value.nil? } if reject_nil_values
39
+ resulting_hash
40
+ end
41
+
42
+ ##
43
+ # Generates a JSON document from the message.
44
+ # Keys are camelized during the process. Null values are not part of the json document.
45
+ #
46
+ # Cucumber::Messages::Duration.new(seconds: 1, nanos: 42).to_json # => '{"seconds":1,"nanos":42}'
47
+ # Cucumber::Messages::PickleTag.new(name: 'foo', ast_node_id: 'abc-def').to_json # => '{"name":"foo","astNodeId":"abc-def"}'
48
+ # Cucumber::Messages::PickleTag.new(name: 'foo', ast_node_id: nil).to_json # => '{"name":"foo"}'
49
+ #
50
+ # As #to_h, the method is recursive
51
+ #
52
+ # location = Cucumber::Messages::Location.new(line: 2)
53
+ # Cucumber::Messages::Comment.new(location: location, text: 'comment').to_json # => '{"location":{"line":2,"column":null},"text":"comment"}'
54
+ ##
55
+ def to_json
56
+ to_h(camelize: true, reject_nil_values: true).to_json
57
+ end
58
+
59
+ private
60
+
61
+ def prepare_value(value, camelize:, reject_nil_values:)
62
+ return value.to_h(camelize: camelize, reject_nil_values: reject_nil_values) if value.is_a?(Cucumber::Messages::Message)
63
+ return value.map { |v| prepare_value(v, camelize: camelize, reject_nil_values: reject_nil_values) } if value.is_a?(Array)
64
+
65
+ value
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,45 @@
1
+ module Cucumber
2
+ module Messages
3
+ class Message
4
+ module Utils
5
+ def self.included(other)
6
+ other.extend(ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+ ##
11
+ # Makes an underscored, lowercase form from the expression in the string.
12
+ #
13
+ # underscore('GherkinDocument') # => "gherkin_document"
14
+ #
15
+ # This is a simplified version of the Ruby on Rails implementation
16
+ # https://github.com/rails/rails/blob/v6.1.3.2/activesupport/lib/active_support/inflector/methods.rb#L92
17
+ ##
18
+ def underscore(term)
19
+ return term unless /[A-Z-]/.match?(term)
20
+
21
+ word = term.gsub(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
22
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
23
+ word.tr!("-", "_")
24
+ word.downcase!
25
+ word
26
+ end
27
+
28
+ ##
29
+ # Converts strings to UpperCamelCase.
30
+ #
31
+ # camelize('gherkin_document') # => "GherkinDocument"
32
+ #
33
+ # This is a simplified version of the Ruby on Rails implementation
34
+ # https://github.com/rails/rails/blob/v6.1.3.2/activesupport/lib/active_support/inflector/methods.rb#L69
35
+ ##
36
+ def camelize(term)
37
+ camelized = term.to_s
38
+ camelized.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
39
+ camelized
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,11 @@
1
+ require 'cucumber/messages/message/deserialization'
2
+ require 'cucumber/messages/message/serialization'
3
+
4
+ module Cucumber
5
+ module Messages
6
+ class Message
7
+ include Cucumber::Messages::Message::Deserialization
8
+ include Cucumber::Messages::Message::Serialization
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ require 'cucumber/messages.deserializers'
2
+
3
+ module Cucumber
4
+ module Messages
5
+ class NdjsonToMessageEnumerator < Enumerator
6
+ def initialize(io)
7
+ super() do |yielder|
8
+ io.each_line do |line|
9
+ next if line.strip.empty?
10
+ begin
11
+ m = Envelope.from_json(line)
12
+ rescue => e
13
+ raise "Not JSON: #{line.strip}"
14
+ end
15
+ yielder.yield(m)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,33 @@
1
+ module Cucumber
2
+ module Messages
3
+ module TimeConversion
4
+ NANOSECONDS_PER_SECOND = 1000000000
5
+
6
+ def time_to_timestamp(time)
7
+ {
8
+ 'seconds' => time.to_i,
9
+ 'nanos' => time.nsec
10
+ }
11
+ end
12
+
13
+ def timestamp_to_time(timestamp)
14
+ Time.at(timestamp['seconds'] + timestamp['nanos'].to_f / NANOSECONDS_PER_SECOND)
15
+ end
16
+
17
+ def seconds_to_duration(seconds_float)
18
+ seconds, second_modulus = seconds_float.divmod(1)
19
+ nanos = second_modulus * NANOSECONDS_PER_SECOND
20
+ {
21
+ 'seconds' => seconds,
22
+ 'nanos' => nanos.to_i
23
+ }
24
+ end
25
+
26
+ def duration_to_seconds(duration)
27
+ seconds_part = duration['seconds']
28
+ nanos_part = duration['nanos'].to_f / NANOSECONDS_PER_SECOND
29
+ seconds_part + nanos_part
30
+ end
31
+ end
32
+ end
33
+ end