graphql 0.9.5 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/base_type.rb +24 -0
- data/lib/graphql/definition_helpers/defined_by_config.rb +5 -4
- data/lib/graphql/definition_helpers/non_null_with_bang.rb +1 -1
- data/lib/graphql/definition_helpers/type_definer.rb +1 -1
- data/lib/graphql/enum_type.rb +16 -3
- data/lib/graphql/input_object_type.rb +10 -0
- data/lib/graphql/language.rb +0 -5
- data/lib/graphql/language/nodes.rb +79 -75
- data/lib/graphql/language/parser.rb +109 -106
- data/lib/graphql/language/transform.rb +100 -91
- data/lib/graphql/language/visitor.rb +78 -74
- data/lib/graphql/list_type.rb +5 -0
- data/lib/graphql/non_null_type.rb +6 -2
- data/lib/graphql/query.rb +58 -9
- data/lib/graphql/query/arguments.rb +29 -26
- data/lib/graphql/query/base_execution/value_resolution.rb +3 -3
- data/lib/graphql/query/directive_chain.rb +1 -1
- data/lib/graphql/query/executor.rb +6 -27
- data/lib/graphql/query/literal_input.rb +89 -0
- data/lib/graphql/query/ruby_input.rb +20 -0
- data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
- data/lib/graphql/query/variables.rb +39 -0
- data/lib/graphql/scalar_type.rb +27 -5
- data/lib/graphql/schema.rb +5 -0
- data/lib/graphql/schema/type_expression.rb +28 -0
- data/lib/graphql/static_validation/literal_validator.rb +5 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +0 -2
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/readme.md +3 -2
- data/spec/graphql/enum_type_spec.rb +7 -2
- data/spec/graphql/input_object_type_spec.rb +45 -0
- data/spec/graphql/language/parser_spec.rb +2 -1
- data/spec/graphql/language/transform_spec.rb +5 -0
- data/spec/graphql/query/base_execution/value_resolution_spec.rb +46 -0
- data/spec/graphql/query/context_spec.rb +37 -0
- data/spec/graphql/query/executor_spec.rb +33 -0
- data/spec/graphql/query_spec.rb +110 -26
- data/spec/graphql/schema/type_expression_spec.rb +38 -0
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +2 -2
- data/spec/support/dairy_app.rb +17 -17
- data/spec/support/dairy_data.rb +2 -2
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9cb8700392b11bc4b4e577e38b424c13b3d33dd9
|
4
|
+
data.tar.gz: 36f2b6d48b2d11084aeb4d0ea50c194e1f2a4b27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8498188be3457f626d74653e48bee19558fc3030f383d8fe65cdf3cfa7f0cf9c8eeabafb1de634a48f9f991de77a0e2630056356de5854af78adbc03881cc14
|
7
|
+
data.tar.gz: de3a1fcfdecefe84e9ed578c9840e861670df763dfb7bd96b11e97e7971c6cbd10c669674cc574904ae9fd0b0db29d70f3dac1bf2aa1f3e430320067dd88859a
|
data/lib/graphql/base_type.rb
CHANGED
@@ -20,6 +20,16 @@ module GraphQL
|
|
20
20
|
self
|
21
21
|
end
|
22
22
|
|
23
|
+
# @return [GraphQL::NonNullType] a non-null version of this type
|
24
|
+
def to_non_null_type
|
25
|
+
GraphQL::NonNullType.new(of_type: self)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [GraphQL::ListType] a list version of this type
|
29
|
+
def to_list_type
|
30
|
+
GraphQL::ListType.new(of_type: self)
|
31
|
+
end
|
32
|
+
|
23
33
|
module ModifiesAnotherType
|
24
34
|
def unwrap
|
25
35
|
self.of_type.unwrap
|
@@ -65,5 +75,19 @@ module GraphQL
|
|
65
75
|
end
|
66
76
|
|
67
77
|
alias :inspect :to_s
|
78
|
+
|
79
|
+
# Coerce `input_value` according to this type's `coerce` method.
|
80
|
+
# Raise an error if the value becomes nil.
|
81
|
+
# @param [Object] Incoming query value
|
82
|
+
# @return [Object] Coerced value for query execution
|
83
|
+
def coerce_input!(input_value)
|
84
|
+
coerced_value = coerce_input(input_value)
|
85
|
+
|
86
|
+
if coerced_value.nil?
|
87
|
+
raise GraphQL::ExecutionError.new("Couldn't coerce #{input_value.inspect} to #{self.unwrap.name}")
|
88
|
+
end
|
89
|
+
|
90
|
+
coerced_value
|
91
|
+
end
|
68
92
|
end
|
69
93
|
end
|
@@ -31,7 +31,9 @@ module GraphQL::DefinitionHelpers::DefinedByConfig
|
|
31
31
|
:possible_types, # interface / union
|
32
32
|
:default_value, # argument
|
33
33
|
:on, # directive
|
34
|
-
:coerce #scalar
|
34
|
+
:coerce, #scalar
|
35
|
+
:coerce_input, #scalar
|
36
|
+
:coerce_result #scalar
|
35
37
|
|
36
38
|
attr_reader :fields, :input_fields, :arguments, :values
|
37
39
|
|
@@ -41,7 +43,7 @@ module GraphQL::DefinitionHelpers::DefinedByConfig
|
|
41
43
|
@on = []
|
42
44
|
@fields = {}
|
43
45
|
@arguments = {}
|
44
|
-
@values =
|
46
|
+
@values = []
|
45
47
|
@input_fields = {}
|
46
48
|
end
|
47
49
|
|
@@ -64,8 +66,7 @@ module GraphQL::DefinitionHelpers::DefinedByConfig
|
|
64
66
|
|
65
67
|
# For EnumType
|
66
68
|
def value(name, desc = nil, deprecation_reason: nil, value: name)
|
67
|
-
|
68
|
-
values[name] = value
|
69
|
+
values << GraphQL::EnumType::EnumValue.new(name: name, description: description, deprecation_reason: deprecation_reason, value: value)
|
69
70
|
end
|
70
71
|
|
71
72
|
# For InputObjectType
|
data/lib/graphql/enum_type.rb
CHANGED
@@ -14,8 +14,17 @@ class GraphQL::EnumType < GraphQL::BaseType
|
|
14
14
|
attr_accessor :name, :description, :values
|
15
15
|
defined_by_config :name, :description, :values
|
16
16
|
|
17
|
+
def values=(values)
|
18
|
+
@values_by_name = {}
|
19
|
+
@values_by_value = {}
|
20
|
+
values.each do |enum_value|
|
21
|
+
@values_by_name[enum_value.name] = enum_value
|
22
|
+
@values_by_value[enum_value.value] = enum_value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
17
26
|
def values
|
18
|
-
@
|
27
|
+
@values_by_name
|
19
28
|
end
|
20
29
|
|
21
30
|
# Define a value within this enum
|
@@ -40,8 +49,12 @@ class GraphQL::EnumType < GraphQL::BaseType
|
|
40
49
|
#
|
41
50
|
# @param value_name [String] the string representation of this enum value
|
42
51
|
# @return [Object] the underlying value for this enum value
|
43
|
-
def
|
44
|
-
|
52
|
+
def coerce_input(value_name)
|
53
|
+
@values_by_name.fetch(value_name).value
|
54
|
+
end
|
55
|
+
|
56
|
+
def coerce_result(value)
|
57
|
+
@values_by_value.fetch(value).name
|
45
58
|
end
|
46
59
|
|
47
60
|
def to_s
|
@@ -18,4 +18,14 @@ class GraphQL::InputObjectType < GraphQL::BaseType
|
|
18
18
|
def kind
|
19
19
|
GraphQL::TypeKinds::INPUT_OBJECT
|
20
20
|
end
|
21
|
+
|
22
|
+
def coerce_input(value)
|
23
|
+
input_values = {}
|
24
|
+
input_fields.each do |input_key, input_field_defn|
|
25
|
+
raw_value = value.fetch(input_key, input_field_defn.default_value)
|
26
|
+
field_type = input_field_defn.type
|
27
|
+
input_values[input_key] = field_type.coerce_input!(raw_value)
|
28
|
+
end
|
29
|
+
GraphQL::Query::Arguments.new(input_values)
|
30
|
+
end
|
21
31
|
end
|
data/lib/graphql/language.rb
CHANGED
@@ -1,89 +1,93 @@
|
|
1
|
-
module GraphQL
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
module GraphQL
|
2
|
+
module Language
|
3
|
+
module Nodes
|
4
|
+
# AbstractNode creates classes who:
|
5
|
+
# - require their keyword arguments, throw ArgumentError if they don't match
|
6
|
+
# - expose accessors for keyword arguments
|
7
|
+
class AbstractNode
|
8
|
+
attr_accessor :line, :col
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
# @param options [Hash] Must contain all attributes defined by {required_attrs}, may also include `position_source`
|
11
|
+
def initialize(options)
|
12
|
+
required_keys = self.class.required_attrs
|
13
|
+
allowed_keys = required_keys + [:line, :col]
|
14
|
+
position_source = options.delete(:position_source)
|
15
|
+
if !position_source.nil?
|
16
|
+
options[:line], options[:col] = position_source.line_and_column
|
17
|
+
end
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
present_keys = options.keys
|
20
|
+
extra_keys = present_keys - allowed_keys
|
21
|
+
if extra_keys.any?
|
22
|
+
raise ArgumentError, "#{self.class.name} Extra arguments: #{extra_keys}"
|
23
|
+
end
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
missing_keys = required_keys - present_keys
|
26
|
+
if missing_keys.any?
|
27
|
+
raise ArgumentError, "#{self.class.name} Missing arguments: #{missing_keys}"
|
28
|
+
end
|
27
29
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
allowed_keys.each do |key|
|
31
|
+
if options.has_key?(key)
|
32
|
+
value = options[key]
|
33
|
+
self.send("#{key}=", value)
|
34
|
+
end
|
35
|
+
end
|
32
36
|
end
|
33
|
-
end
|
34
|
-
end
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
# Test all attributes, checking for any other nodes below this one
|
39
|
+
def children
|
40
|
+
self.class.required_attrs
|
41
|
+
.map { |attr| send(attr) }
|
42
|
+
.flatten
|
43
|
+
.select { |val| val.is_a?(GraphQL::Language::Nodes::AbstractNode) }
|
44
|
+
end
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
class << self
|
47
|
+
attr_reader :required_attrs
|
48
|
+
# Defines attributes which are required at initialization.
|
49
|
+
def attr_required(*attr_names)
|
50
|
+
@required_attrs ||= []
|
51
|
+
@required_attrs += attr_names
|
52
|
+
attr_accessor(*attr_names)
|
53
|
+
end
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
55
|
+
# Create a new AbstractNode child which
|
56
|
+
# requires and exposes {attr_names}.
|
57
|
+
# @param attr_names [Array<Symbol>] Attributes this node class will have
|
58
|
+
# @param block [Block] Block passed to `Class.new`
|
59
|
+
# @return [Class] A new node class
|
60
|
+
def create(*attr_names, &block)
|
61
|
+
cls = Class.new(self, &block)
|
62
|
+
cls.attr_required(*attr_names)
|
63
|
+
cls
|
64
|
+
end
|
65
|
+
end
|
62
66
|
end
|
63
|
-
end
|
64
|
-
end
|
65
67
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
68
|
+
Argument = AbstractNode.create(:name, :value)
|
69
|
+
Directive = AbstractNode.create(:name, :arguments)
|
70
|
+
Document = AbstractNode.create(:parts)
|
71
|
+
Enum = AbstractNode.create(:name)
|
72
|
+
Field = AbstractNode.create(:name, :alias, :arguments, :directives, :selections)
|
73
|
+
FragmentDefinition = AbstractNode.create(:name, :type, :directives, :selections)
|
74
|
+
FragmentSpread = AbstractNode.create(:name, :directives)
|
75
|
+
InlineFragment = AbstractNode.create(:type, :directives, :selections)
|
76
|
+
InputObject = AbstractNode.create(:pairs) do
|
77
|
+
def to_h(options={})
|
78
|
+
pairs.inject({}) do |memo, pair|
|
79
|
+
v = pair.value
|
80
|
+
memo[pair.name] = v.is_a?(InputObject) ? v.to_h : v
|
81
|
+
memo
|
82
|
+
end
|
83
|
+
end
|
80
84
|
end
|
85
|
+
ListType = AbstractNode.create(:of_type)
|
86
|
+
NonNullType = AbstractNode.create(:of_type)
|
87
|
+
OperationDefinition = AbstractNode.create(:operation_type, :name, :variables, :directives, :selections)
|
88
|
+
TypeName = AbstractNode.create(:name)
|
89
|
+
Variable = AbstractNode.create(:name, :type, :default_value)
|
90
|
+
VariableIdentifier = AbstractNode.create(:name)
|
81
91
|
end
|
82
92
|
end
|
83
|
-
ListType = AbstractNode.create(:of_type)
|
84
|
-
NonNullType = AbstractNode.create(:of_type)
|
85
|
-
OperationDefinition = AbstractNode.create(:operation_type, :name, :variables, :directives, :selections)
|
86
|
-
TypeName = AbstractNode.create(:name)
|
87
|
-
Variable = AbstractNode.create(:name, :type, :default_value)
|
88
|
-
VariableIdentifier = AbstractNode.create(:name)
|
89
93
|
end
|
@@ -1,118 +1,121 @@
|
|
1
|
-
module GraphQL
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
module GraphQL
|
2
|
+
module Language
|
3
|
+
# Parser is a [parslet](http://kschiess.github.io/parslet/) parser for parsing queries.
|
4
|
+
#
|
5
|
+
class Parser < Parslet::Parser
|
6
|
+
root(:document)
|
7
|
+
rule(:document) { (
|
8
|
+
space |
|
9
|
+
operation_definition |
|
10
|
+
fragment_definition
|
11
|
+
).repeat.as(:document_parts)
|
12
|
+
}
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
# TODO: whitespace sensitive regarding `on`, eg `onFood`, see lookahead note in spec
|
15
|
+
rule(:fragment_definition) {
|
16
|
+
str("fragment").as(:fragment_keyword) >>
|
17
|
+
space? >> name.as(:fragment_name) >>
|
18
|
+
space? >> str("on") >> space? >> name.as(:type_condition) >>
|
19
|
+
space? >> directives.maybe.as(:optional_directives).as(:directives) >>
|
20
|
+
space? >> selections.as(:selections)
|
21
|
+
}
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
23
|
+
rule(:fragment_spread) {
|
24
|
+
spread.as(:fragment_spread_keyword) >> space? >>
|
25
|
+
name.as(:fragment_spread_name) >> space? >>
|
26
|
+
directives.maybe.as(:optional_directives).as(:directives)
|
27
|
+
}
|
28
|
+
rule(:spread) { str("...") }
|
29
|
+
# TODO: `on` bug, see spec
|
30
|
+
rule(:inline_fragment) {
|
31
|
+
spread.as(:fragment_spread_keyword) >> space? >>
|
32
|
+
str("on ") >> name.as(:inline_fragment_type) >> space? >>
|
33
|
+
directives.maybe.as(:optional_directives).as(:directives) >> space? >>
|
34
|
+
selections.as(:selections)
|
35
|
+
}
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
37
|
+
rule(:operation_definition) { (unnamed_selections | typed_operation_definition) }
|
38
|
+
rule(:unnamed_selections) { selections.as(:unnamed_selections)}
|
39
|
+
rule(:typed_operation_definition) {
|
40
|
+
operation_type.as(:operation_type) >> space? >>
|
41
|
+
name.maybe.as(:name) >> space? >>
|
42
|
+
operation_variable_definitions.maybe.as(:optional_variables).as(:variables) >> space? >>
|
43
|
+
directives.maybe.as(:optional_directives).as(:directives) >> space? >>
|
44
|
+
selections.as(:selections)
|
45
|
+
}
|
46
|
+
rule(:operation_type) { (str("query") | str("mutation")) }
|
47
|
+
rule(:operation_variable_definitions) { str("(") >> space? >> (operation_variable_definition >> separator?).repeat(1) >> space? >> str(")") }
|
48
|
+
rule(:operation_variable_definition) {
|
49
|
+
value_variable.as(:variable_name) >> space? >>
|
50
|
+
str(":") >> space? >>
|
51
|
+
type.as(:variable_type) >> space? >>
|
52
|
+
(str("=") >> space? >> value.as(:variable_default_value)).maybe.as(:variable_optional_default_value)
|
53
|
+
}
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
+
rule(:selection) { (inline_fragment | fragment_spread | field) >> space? >> separator? }
|
56
|
+
rule(:selections) { str("{") >> space? >> selection.repeat(1) >> space? >> str("}")}
|
55
57
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
58
|
+
rule(:field) {
|
59
|
+
field_alias.maybe.as(:alias) >>
|
60
|
+
name.as(:field_name) >>
|
61
|
+
field_arguments.maybe.as(:optional_field_arguments).as(:field_arguments) >> space? >>
|
62
|
+
directives.maybe.as(:optional_directives).as(:directives) >> space? >>
|
63
|
+
selections.maybe.as(:optional_selections).as(:selections)
|
64
|
+
}
|
63
65
|
|
64
|
-
|
65
|
-
|
66
|
-
|
66
|
+
rule(:field_alias) { name.as(:alias_name) >> space? >> str(":") >> space? }
|
67
|
+
rule(:field_arguments) { str("(") >> field_argument.repeat(1) >> str(")") }
|
68
|
+
rule(:field_argument) { name.as(:field_argument_name) >> str(":") >> space? >> value.as(:field_argument_value) >> separator? }
|
67
69
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
70
|
+
rule(:directives) { (directive >> separator?).repeat(1) }
|
71
|
+
rule(:directive) {
|
72
|
+
str("@") >> name.as(:directive_name) >>
|
73
|
+
directive_arguments.maybe.as(:optional_directive_arguments).as(:directive_arguments)
|
74
|
+
}
|
75
|
+
rule(:directive_arguments) { str("(") >> directive_argument.repeat(1) >> str(")") }
|
76
|
+
rule(:directive_argument) { name.as(:directive_argument_name) >> str(":") >> space? >> value.as(:directive_argument_value) >> separator? }
|
75
77
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
78
|
+
rule(:type) { (non_null_type | list_type | type_name)}
|
79
|
+
rule(:list_type) { str("[") >> type.as(:list_type) >> str("]")}
|
80
|
+
rule(:non_null_type) { (list_type | type_name).as(:non_null_type) >> str("!") }
|
81
|
+
rule(:type_name) { name.as(:type_name) }
|
80
82
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
83
|
+
rule(:value) {(
|
84
|
+
value_input_object |
|
85
|
+
value_float |
|
86
|
+
value_int |
|
87
|
+
value_string |
|
88
|
+
value_boolean |
|
89
|
+
value_array |
|
90
|
+
value_variable |
|
91
|
+
value_enum
|
92
|
+
)}
|
93
|
+
rule(:value_sign?) { match('[\-\+]').maybe }
|
94
|
+
rule(:value_array) { (str("[") >> (value >> separator?).repeat(0) >> str("]")).as(:array) }
|
95
|
+
rule(:value_boolean) { (str("true") | str("false")).as(:boolean) }
|
96
|
+
rule(:value_float) { (value_sign? >> match('\d').repeat(1) >> str(".") >> match('\d').repeat(1) >> (match("[eE]") >> value_sign? >> match('\d').repeat(1)).maybe).as(:float) }
|
97
|
+
rule(:value_input_object) { str("{") >> space? >> value_input_object_pair.repeat(1).as(:input_object) >> space? >> str("}") }
|
98
|
+
rule(:value_input_object_pair) { space? >> name.as(:input_object_name) >> space? >> str(":") >> space? >> value.as(:input_object_value) >> separator? }
|
99
|
+
rule(:value_int) { (value_sign? >> match('\d').repeat(1)).as(:int) }
|
100
|
+
rule(:value_string) { str('"') >> value_string_char.repeat.as(:string) >> str('"')}
|
101
|
+
rule(:value_string_char) { value_string_escaped_char | value_string_escaped_unicode | value_string_source_char}
|
102
|
+
rule(:value_string_escaped_char) { str("\\") >> match('["\/bfnrt]') }
|
103
|
+
rule(:value_string_escaped_unicode) { str("\\") >> match('u[\dA-Fa-f]{4}')}
|
104
|
+
rule(:value_string_source_char) { (str('"') | str("\\") | value_string_line_terminator).absent? >> any }
|
105
|
+
rule(:value_string_line_terminator) {
|
106
|
+
str('\u000A') | # new line
|
107
|
+
str('\u000D') | # carriage return
|
108
|
+
str('\u2028') | # line separator
|
109
|
+
str('\u2029') # paragraph separator
|
110
|
+
}
|
111
|
+
rule(:value_enum) { name.as(:enum) }
|
112
|
+
rule(:value_variable) { str("$") >> name.as(:variable) }
|
111
113
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
114
|
+
rule(:separator?) { space? >> str(",").maybe >> space? }
|
115
|
+
rule(:name) { match('[_A-Za-z]') >> match('[_0-9A-Za-z]').repeat(0) }
|
116
|
+
rule(:comment) { str("#") >> match('[^\r\n]').repeat(0) }
|
117
|
+
rule(:space) { (match('[\s\n]+') | comment).repeat(1) }
|
118
|
+
rule(:space?) { space.maybe }
|
119
|
+
end
|
117
120
|
end
|
118
121
|
end
|