functions_framework 0.4.1 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 817129c1773079fed039152996a18e91a054241940b8d6e3437cb66e10046304
4
- data.tar.gz: ff3f61597726b60241e8b528ef520641fa3f08ddb7adcdfb32f9413fcb125f45
3
+ metadata.gz: 370556a72cd31e4d24499b0715ca7f96a0598cb856fb28fed3d3eaabee0a80ae
4
+ data.tar.gz: 04a24cf1e2f205fe124eb0bda891f647177b93c43ba1c2286ed52ffbad18cb1d
5
5
  SHA512:
6
- metadata.gz: 7396117d6e94bec87c70c697c7364b9256c7575f61c7259eb25df65694e9627b6c33a93e0caaea5d9d7955707e731eaddc27b90d4595f0029d181fc6cc18bfdf
7
- data.tar.gz: 8eb929ace2a49ae764f5de0fef6dd7e0d668c80864368ad2379d0e52a89a44c7a3514e92dc02802d940374808a271a89f3076f1c8f3d0a532204118615e0c1af
6
+ metadata.gz: 34d91fa0417632a253ddba1db7aa85f54697eb645caaaf96c9253a36fca241bca80bf17a40796dd5c8521d2c7bbe04ff23353764491d28b5e4335e0ee022f05a
7
+ data.tar.gz: 0d8d59ef3e45d3980136d7015e8066052bffe3942c442f715368dcf6ac5dd98b0cb282c313fa1a387ac70514747701e75c879c549faa9802444fe6b4d56115c7
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ### v0.5.0 / 2020-07-09
4
+
5
+ * Removed embedded CloudEvents classes and added the official CloudEvents SDK as a dependency. A `FunctionsFramework::CloudEvents` alias provides backward compatibility.
6
+
3
7
  ### v0.4.1 / 2020-07-08
4
8
 
5
9
  * Fixed unsupported signal error on Windows.
data/README.md CHANGED
@@ -60,7 +60,7 @@ Create a `Gemfile` listing the Functions Framework as a dependency:
60
60
  ```ruby
61
61
  # Gemfile
62
62
  source "https://rubygems.org"
63
- gem "functions_framework", "~> 0.4"
63
+ gem "functions_framework", "~> 0.5"
64
64
  ```
65
65
 
66
66
  Create a file called `app.rb` and include the following code. This defines a
@@ -64,7 +64,7 @@ Create a `Gemfile` listing the Functions Framework as a dependency:
64
64
  ```ruby
65
65
  # Gemfile
66
66
  source "https://rubygems.org"
67
- gem "functions_framework", "~> 0.4"
67
+ gem "functions_framework", "~> 0.5"
68
68
  ```
69
69
 
70
70
  Create a file called `app.rb` and include the following code. This defines a
@@ -112,7 +112,7 @@ dependency on Sinatra in your `Gemfile`:
112
112
  ```ruby
113
113
  # Gemfile
114
114
  source "https://rubygems.org"
115
- gem "functions_framework", "~> 0.4"
115
+ gem "functions_framework", "~> 0.5"
116
116
  gem "sinatra", "~> 2.0"
117
117
  ```
118
118
 
@@ -158,9 +158,9 @@ end
158
158
  ```
159
159
 
160
160
  The event parameter will be either a
161
- [CloudEvents V0.3 Event](https://rubydoc.info/gems/functions_framework/FunctionsFramework/CloudEvents/Event/V0)
161
+ [CloudEvents V0.3 Event](https://rubydoc.info/gems/cloud_events/CloudEvents/Event/V0)
162
162
  object ([see spec](https://github.com/cloudevents/spec/blob/v0.3/spec.md)) or a
163
- [CloudEvents V1.0 Event](https://rubydoc.info/gems/functions_framework/FunctionsFramework/CloudEvents/Event/V1)
163
+ [CloudEvents V1.0 Event](https://rubydoc.info/gems/cloud_events/CloudEvents/Event/V1)
164
164
  object ([see spec](https://github.com/cloudevents/spec/blob/v1.0/spec.md)).
165
165
 
166
166
  Some Google Cloud services send events in a legacy event format that was defined
@@ -236,7 +236,7 @@ A simple project might look like this:
236
236
  ```ruby
237
237
  # Gemfile
238
238
  source "https://rubygems.org"
239
- gem "functions_framework", "~> 0.4"
239
+ gem "functions_framework", "~> 0.5"
240
240
  ```
241
241
 
242
242
  ```ruby
@@ -14,7 +14,8 @@
14
14
 
15
15
  require "logger"
16
16
 
17
- require "functions_framework/cloud_events"
17
+ require "cloud_events"
18
+
18
19
  require "functions_framework/function"
19
20
  require "functions_framework/legacy_event_converter"
20
21
  require "functions_framework/registry"
@@ -44,10 +45,6 @@ require "functions_framework/version"
44
45
  #
45
46
  # Here is a roadmap to the internal modules in the Ruby functions framework.
46
47
  #
47
- # * {FunctionsFramework::CloudEvents} provides an implementation of the
48
- # [CloudEvents](https://cloudevents.io) specification. In particular, if
49
- # you define an event function, you will receive the event as a
50
- # {FunctionsFramework::CloudEvents::Event} object.
51
48
  # * {FunctionsFramework::CLI} is the implementation of the
52
49
  # `functions-framework-ruby` executable. Most apps will not need to interact
53
50
  # with this class directly.
@@ -94,6 +91,12 @@ module FunctionsFramework
94
91
  #
95
92
  DEFAULT_SOURCE = "./app.rb".freeze
96
93
 
94
+ ##
95
+ # The CloudEvents implementation was extracted to become the official
96
+ # CloudEvents SDK. This alias is left here for backward compatibility.
97
+ #
98
+ CloudEvents = ::CloudEvents
99
+
97
100
  class << self
98
101
  ##
99
102
  # The "global" registry that holds events defined by the
@@ -144,7 +147,8 @@ module FunctionsFramework
144
147
  #
145
148
  # You must provide a name for the function, and a block that implemets the
146
149
  # function. The block should take one argument: the event object of type
147
- # {FunctionsFramework::CloudEvents::Event}. Any return value is ignored.
150
+ # [`CloudEvents::Event`](https://rubydoc.info/gems/cloud_events/CloudEvents/Event).
151
+ # Any return value is ignored.
148
152
  #
149
153
  # ## Example
150
154
  #
@@ -23,8 +23,9 @@ module FunctionsFramework
23
23
  # `Rack::Request` argument and returns one of various HTTP response types.
24
24
  # See {FunctionsFramework::Registry.add_http}. For a function of type
25
25
  # `:cloud_event`, the `call` method takes a single
26
- # {FunctionsFramework::CloudEvents::Event CloudEvent} argument, and does not
27
- # return a value. See {FunctionsFramework::Registry.add_cloud_event}.
26
+ # [CloudEvent](https://rubydoc.info/gems/cloud_events/CloudEvents/Event)
27
+ # argument, and does not return a value.
28
+ # See {FunctionsFramework::Registry.add_cloud_event}.
28
29
  #
29
30
  # If a callable object is provided directly, its `call` method is invoked for
30
31
  # every function execution. Note that this means it may be called multiple
@@ -23,12 +23,11 @@ module FunctionsFramework
23
23
  # Decode an event from the given Rack environment hash.
24
24
  #
25
25
  # @param env [Hash] The Rack environment
26
- # @return [FunctionsFramework::CloudEvents::Event] if the request could
27
- # be converted
26
+ # @return [::CloudEvents::Event] if the request could be converted
28
27
  # @return [nil] if the event format was not recognized.
29
28
  #
30
29
  def decode_rack_env env
31
- content_type = CloudEvents::ContentType.new env["CONTENT_TYPE"]
30
+ content_type = ::CloudEvents::ContentType.new env["CONTENT_TYPE"]
32
31
  return nil unless content_type.media_type == "application" && content_type.subtype_base == "json"
33
32
  input = read_input_json env["rack.input"], content_type.charset
34
33
  return nil unless input
@@ -85,14 +84,14 @@ module FunctionsFramework
85
84
  return nil unless type && source
86
85
  ce_data = convert_data context[:service], data
87
86
  content_type = "application/json; charset=#{charset}"
88
- CloudEvents::Event.new id: context[:id],
89
- source: source,
90
- type: type,
91
- spec_version: "1.0",
92
- data_content_type: content_type,
93
- data: ce_data,
94
- subject: subject,
95
- time: context[:timestamp]
87
+ ::CloudEvents::Event.new id: context[:id],
88
+ source: source,
89
+ type: type,
90
+ spec_version: "1.0",
91
+ data_content_type: content_type,
92
+ data: ce_data,
93
+ subject: subject,
94
+ time: context[:timestamp]
96
95
  end
97
96
 
98
97
  def convert_source service, resource
@@ -80,7 +80,8 @@ module FunctionsFramework
80
80
  #
81
81
  # You must provide a name for the function, and a block that implemets the
82
82
  # function. The block should take _one_ argument: the event object of type
83
- # {FunctionsFramework::CloudEvents::Event}. Any return value is ignored.
83
+ # [`CloudEvents::Event`](https://rubydoc.info/gems/cloud_events/CloudEvents/Event).
84
+ # Any return value is ignored.
84
85
  #
85
86
  # @param name [String] The function name
86
87
  # @param block [Proc] The function code as a proc
@@ -344,7 +344,7 @@ module FunctionsFramework
344
344
  string_response response, "text/plain", 200
345
345
  when ::Hash
346
346
  string_response ::JSON.dump(response), "application/json", 200
347
- when CloudEvents::CloudEventsError
347
+ when ::CloudEvents::CloudEventsError
348
348
  cloud_events_error_response response
349
349
  when ::StandardError
350
350
  error_response "#{response.class}: #{response.message}\n#{response.backtrace}\n"
@@ -405,7 +405,7 @@ module FunctionsFramework
405
405
  def initialize function, config
406
406
  super config
407
407
  @function = function
408
- @cloud_events = CloudEvents::HttpBinding.default
408
+ @cloud_events = ::CloudEvents::HttpBinding.default
409
409
  @legacy_events = LegacyEventConverter.new
410
410
  end
411
411
 
@@ -415,11 +415,11 @@ module FunctionsFramework
415
415
  event = decode_event env
416
416
  response =
417
417
  case event
418
- when CloudEvents::Event
418
+ when ::CloudEvents::Event
419
419
  handle_cloud_event event, logger
420
420
  when ::Array
421
- CloudEvents::HttpContentError.new "Batched CloudEvents are not supported"
422
- when CloudEvents::CloudEventsError
421
+ ::CloudEvents::HttpContentError.new "Batched CloudEvents are not supported"
422
+ when ::CloudEvents::CloudEventsError
423
423
  event
424
424
  else
425
425
  raise "Unexpected event type: #{event.class}"
@@ -432,8 +432,8 @@ module FunctionsFramework
432
432
  def decode_event env
433
433
  @cloud_events.decode_rack_env(env) ||
434
434
  @legacy_events.decode_rack_env(env) ||
435
- raise(CloudEvents::HttpContentError, "Unrecognized event format")
436
- rescue CloudEvents::CloudEventsError => e
435
+ raise(::CloudEvents::HttpContentError, "Unrecognized event format")
436
+ rescue ::CloudEvents::CloudEventsError => e
437
437
  e
438
438
  end
439
439
 
@@ -100,7 +100,7 @@ module FunctionsFramework
100
100
  # be of type :cloud_event`.
101
101
  #
102
102
  # @param name [String] The name of the function to call
103
- # @param event [FunctionsFramework::CloudEvets::Event] The event to send
103
+ # @param event [::CloudEvents::Event] The event to send
104
104
  # @return [nil]
105
105
  #
106
106
  def call_event name, event
@@ -174,23 +174,35 @@ module FunctionsFramework
174
174
  # @param source [String,URI] Event source (optional)
175
175
  # @param type [String] Event type (optional)
176
176
  # @param spec_version [String] Spec version (optional)
177
- # @param data_content_type [String,FunctionsFramework::CloudEvents::ContentType]
177
+ # @param data_content_type [String,::CloudEvents::ContentType]
178
178
  # Content type for the data (optional)
179
179
  # @param data_schema [String,URI] Data schema (optional)
180
180
  # @param subject [String] Subject (optional)
181
181
  # @param time [String,DateTime] Event timestamp (optional)
182
- # @return [FunctionsFramework::CloudEvents::Event]
182
+ # @return [::CloudEvents::Event]
183
183
  #
184
184
  def make_cloud_event data,
185
- id: nil, source: nil, type: nil, spec_version: nil,
186
- data_content_type: nil, data_schema: nil, subject: nil, time: nil
185
+ id: nil,
186
+ source: nil,
187
+ type: nil,
188
+ spec_version: nil,
189
+ data_content_type: nil,
190
+ data_schema: nil,
191
+ subject: nil,
192
+ time: nil
187
193
  id ||= "random-id-#{rand 100_000_000}"
188
194
  source ||= "functions-framework-testing"
189
195
  type ||= "com.example.test"
190
196
  spec_version ||= "1.0"
191
- CloudEvents::Event.new id: id, source: source, type: type, spec_version: spec_version,
192
- data_content_type: data_content_type, data_schema: data_schema,
193
- subject: subject, time: time, data: data
197
+ ::CloudEvents::Event.new id: id,
198
+ source: source,
199
+ type: type,
200
+ spec_version: spec_version,
201
+ data_content_type: data_content_type,
202
+ data_schema: data_schema,
203
+ subject: subject,
204
+ time: time,
205
+ data: data
194
206
  end
195
207
 
196
208
  extend self
@@ -17,5 +17,5 @@ module FunctionsFramework
17
17
  # Version of the Ruby Functions Framework
18
18
  # @return [String]
19
19
  #
20
- VERSION = "0.4.1".freeze
20
+ VERSION = "0.5.0".freeze
21
21
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: functions_framework
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Azuma
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-08 00:00:00.000000000 Z
11
+ date: 2020-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: cloud_events
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.1'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: puma
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -60,15 +74,6 @@ files:
60
74
  - docs/writing-functions.md
61
75
  - lib/functions_framework.rb
62
76
  - lib/functions_framework/cli.rb
63
- - lib/functions_framework/cloud_events.rb
64
- - lib/functions_framework/cloud_events/content_type.rb
65
- - lib/functions_framework/cloud_events/errors.rb
66
- - lib/functions_framework/cloud_events/event.rb
67
- - lib/functions_framework/cloud_events/event/field_interpreter.rb
68
- - lib/functions_framework/cloud_events/event/v0.rb
69
- - lib/functions_framework/cloud_events/event/v1.rb
70
- - lib/functions_framework/cloud_events/http_binding.rb
71
- - lib/functions_framework/cloud_events/json_format.rb
72
77
  - lib/functions_framework/function.rb
73
78
  - lib/functions_framework/legacy_event_converter.rb
74
79
  - lib/functions_framework/registry.rb
@@ -82,7 +87,7 @@ metadata:
82
87
  changelog_uri: https://github.com/GoogleCloudPlatform/functions-framework-ruby/blob/master/CHANGELOG.md
83
88
  source_code_uri: https://github.com/GoogleCloudPlatform/functions-framework-ruby
84
89
  bug_tracker_uri: https://github.com/GoogleCloudPlatform/functions-framework-ruby/issues
85
- documentation_uri: https://rubydoc.info/gems/functions_framework/0.4.1
90
+ documentation_uri: https://rubydoc.info/gems/functions_framework/0.5.0
86
91
  post_install_message:
87
92
  rdoc_options: []
88
93
  require_paths:
@@ -1,45 +0,0 @@
1
- # Copyright 2020 Google LLC
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # https://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- require "functions_framework/cloud_events/content_type"
16
- require "functions_framework/cloud_events/errors"
17
- require "functions_framework/cloud_events/event"
18
- require "functions_framework/cloud_events/http_binding"
19
- require "functions_framework/cloud_events/json_format"
20
-
21
- module FunctionsFramework
22
- ##
23
- # CloudEvents implementation.
24
- #
25
- # This is a Ruby implementation of the [CloudEvents](https://cloudevents.io)
26
- # specification. It supports both
27
- # [CloudEvents 0.3](https://github.com/cloudevents/spec/blob/v0.3/spec.md) and
28
- # [CloudEvents 1.0](https://github.com/cloudevents/spec/blob/v1.0/spec.md).
29
- #
30
- module CloudEvents
31
- # @private
32
- SUPPORTED_SPEC_VERSIONS = ["0.3", "1.0"].freeze
33
-
34
- class << self
35
- ##
36
- # The spec versions supported by this implementation.
37
- #
38
- # @return [Array<String>]
39
- #
40
- def supported_spec_versions
41
- SUPPORTED_SPEC_VERSIONS
42
- end
43
- end
44
- end
45
- end
@@ -1,222 +0,0 @@
1
- # Copyright 2020 Google LLC
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # https://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- module FunctionsFramework
16
- module CloudEvents
17
- ##
18
- # A parsed content-type header.
19
- #
20
- # This object represents the information contained in a Content-Type,
21
- # obtained by parsing the header according to RFC 2045.
22
- #
23
- # Case-insensitive fields, such as media_type and subtype, are normalized
24
- # to lower case.
25
- #
26
- # If parsing fails, this class will try to get as much information as it
27
- # can, and fill the rest with defaults as recommended in RFC 2045 sec 5.2.
28
- # In case of a parsing error, the {#error_message} field will be set.
29
- #
30
- class ContentType
31
- ##
32
- # Parse the given header value.
33
- #
34
- # @param string [String] Content-Type header value in RFC 2045 format
35
- #
36
- def initialize string
37
- @string = string
38
- @media_type = "text"
39
- @subtype_base = @subtype = "plain"
40
- @subtype_format = nil
41
- @params = []
42
- @charset = "us-ascii"
43
- @error_message = nil
44
- parse consume_comments string.strip
45
- @canonical_string = "#{@media_type}/#{@subtype}" +
46
- @params.map { |k, v| "; #{k}=#{maybe_quote v}" }.join
47
- end
48
-
49
- ##
50
- # The original header content string.
51
- # @return [String]
52
- #
53
- attr_reader :string
54
- alias to_s string
55
-
56
- ##
57
- # A "canonical" header content string with spacing and capitalization
58
- # normalized.
59
- # @return [String]
60
- #
61
- attr_reader :canonical_string
62
-
63
- ##
64
- # The media type.
65
- # @return [String]
66
- #
67
- attr_reader :media_type
68
-
69
- ##
70
- # The entire content subtype (which could include an extension delimited
71
- # by a plus sign).
72
- # @return [String]
73
- #
74
- attr_reader :subtype
75
-
76
- ##
77
- # The portion of the content subtype before any plus sign.
78
- # @return [String]
79
- #
80
- attr_reader :subtype_base
81
-
82
- ##
83
- # The portion of the content subtype after any plus sign, or nil if there
84
- # is no plus sign in the subtype.
85
- # @return [String,nil]
86
- #
87
- attr_reader :subtype_format
88
-
89
- ##
90
- # An array of parameters, each element as a two-element array of the
91
- # parameter name and value.
92
- # @return [Array<Array(String,String)>]
93
- #
94
- attr_reader :params
95
-
96
- ##
97
- # The charset, defaulting to "us-ascii" if none is explicitly set.
98
- # @return [String]
99
- #
100
- attr_reader :charset
101
-
102
- ##
103
- # The error message when parsing, or `nil` if there was no error message.
104
- # @return [String,nil]
105
- #
106
- attr_reader :error_message
107
-
108
- ##
109
- # An array of values for the given parameter name
110
- # @param key [String]
111
- # @return [Array<String>]
112
- #
113
- def param_values key
114
- key = key.downcase
115
- @params.inject([]) { |a, (k, v)| key == k ? a << v : a }
116
- end
117
-
118
- ## @private
119
- def == other
120
- other.is_a?(ContentType) && canonical_string == other.canonical_string
121
- end
122
- alias eql? ==
123
-
124
- ## @private
125
- def hash
126
- canonical_string.hash
127
- end
128
-
129
- ## @private
130
- class ParseError < ::StandardError
131
- end
132
-
133
- private
134
-
135
- def parse str
136
- @media_type, str = consume_token str, downcase: true, error_message: "Failed to parse media type"
137
- str = consume_special str, "/"
138
- @subtype, str = consume_token str, downcase: true, error_message: "Failed to parse subtype"
139
- @subtype_base, @subtype_format = @subtype.split "+", 2
140
- until str.empty?
141
- str = consume_special str, ";"
142
- name, str = consume_token str, downcase: true, error_message: "Faled to parse attribute name"
143
- str = consume_special str, "=", error_message: "Failed to find value for attribute #{name}"
144
- val, str = consume_token_or_quoted str, error_message: "Failed to parse value for attribute #{name}"
145
- @params << [name, val]
146
- @charset = val if name == "charset"
147
- end
148
- rescue ParseError => e
149
- @error_message = e.message
150
- end
151
-
152
- def consume_token str, downcase: false, error_message: nil
153
- match = /^([\w!#\$%&'\*\+\.\^`\{\|\}-]+)(.*)$/.match str
154
- raise ParseError, error_message || "Expected token" unless match
155
- token = match[1]
156
- token.downcase! if downcase
157
- str = consume_comments match[2].strip
158
- [token, str]
159
- end
160
-
161
- def consume_special str, expected, error_message: nil
162
- raise ParseError, error_message || "Expected #{expected.inspect}" unless str.start_with? expected
163
- consume_comments str[1..-1].strip
164
- end
165
-
166
- def consume_token_or_quoted str, error_message: nil
167
- return consume_token str unless str.start_with? '"'
168
- arr = []
169
- index = 1
170
- loop do
171
- char = str[index]
172
- case char
173
- when nil
174
- raise ParseError, error_message || "Quoted-string never finished"
175
- when "\""
176
- break
177
- when "\\"
178
- char = str[index + 1]
179
- raise ParseError, error_message || "Quoted-string never finished" unless char
180
- arr << char
181
- index += 2
182
- else
183
- arr << char
184
- index += 1
185
- end
186
- end
187
- index += 1
188
- str = consume_comments str[index..-1].strip
189
- [arr.join, str]
190
- end
191
-
192
- def consume_comments str
193
- return str unless str.start_with? "("
194
- index = 1
195
- loop do
196
- char = str[index]
197
- case char
198
- when nil
199
- raise ParseError, "Comment never finished"
200
- when ")"
201
- break
202
- when "\\"
203
- index += 2
204
- when "("
205
- str = consume_comments str[index..-1]
206
- index = 0
207
- else
208
- index += 1
209
- end
210
- end
211
- index += 1
212
- consume_comments str[index..-1].strip
213
- end
214
-
215
- def maybe_quote str
216
- return str if /^[\w!#\$%&'\*\+\.\^`\{\|\}-]+$/ =~ str
217
- str = str.gsub("\\", "\\\\\\\\").gsub("\"", "\\\\\"")
218
- "\"#{str}\""
219
- end
220
- end
221
- end
222
- end