cucumber-messages 0.0.0

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