yard 0.9.43 → 0.9.44
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 +4 -4
- data/CHANGELOG.md +9 -0
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/docs/Tags.md +22 -1
- data/lib/yard/autoload.rb +1 -0
- data/lib/yard/server/commands/base.rb +3 -9
- data/lib/yard/server/static_caching.rb +36 -5
- data/lib/yard/tags/types_explainer.rb +164 -39
- data/lib/yard/templates/helpers/markup/html_entities.rb +2140 -0
- data/lib/yard/templates/helpers/markup/hybrid_markdown.rb +2 -18
- data/lib/yard/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 87aef8f8a0b6c25d1a2c05214992313aa6278659214af6650d07876346b771ec
|
|
4
|
+
data.tar.gz: 149cee78996a555a97d7223252df845be60ecaf9bf92d7c9bedeeb8fe14952bf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e7bd349e0d31fb2b79dd0c18a057ff9323206c4d6be3535f902f9fa45ee30a8649bd3036861fdf2ab90e017bfaf7622eb744461a2728a88b68792bc8acbd3fa9
|
|
7
|
+
data.tar.gz: 2f4e486d27daf722a02c2efc2e7af568c12a76ebd30b23871a46c25c54e03f9e6f51397953dac330dba6b0581b7088882e581b04f43fe9ef64e3a0a93862df15
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# main
|
|
2
2
|
|
|
3
|
+
# [0.9.44] - May 25th, 2026
|
|
4
|
+
|
|
5
|
+
[0.9.44]: https://github.com/lsegal/yard/compare/v0.9.43...v0.9.44
|
|
6
|
+
|
|
7
|
+
- Fix possible path traversal with document_root (`--docroot`) and disk caching (`--cache`) set in `yard server` ([GHSA-pxcc-8665-phx8](https://github.com/lsegal/yard/security/advisories/GHSA-pxcc-8665-phx8))
|
|
8
|
+
- Fix support for HTML entities in HybridMarkup (#1680, #1681)
|
|
9
|
+
- Add support for string literals in TypesExplainer (#1628)
|
|
10
|
+
- Add support for multiple & nested Hash keys definition (#1630)
|
|
11
|
+
|
|
3
12
|
# [0.9.43] - April 17th, 2026
|
|
4
13
|
|
|
5
14
|
[0.9.43]: https://github.com/lsegal/yard/compare/v0.9.42...v0.9.43
|
data/LICENSE
CHANGED
data/README.md
CHANGED
|
@@ -323,7 +323,7 @@ See {file:CHANGELOG.md} for a list of changes.
|
|
|
323
323
|
|
|
324
324
|
## License
|
|
325
325
|
|
|
326
|
-
YARD ©
|
|
326
|
+
YARD © by [Loren Segal](mailto:lsegal@soen.ca). YARD is licensed
|
|
327
327
|
under the MIT license except for some files which come from the RDoc/Ruby
|
|
328
328
|
distributions. Please see the {file:LICENSE} and {file:LEGAL} documents for more
|
|
329
329
|
information.
|
data/docs/Tags.md
CHANGED
|
@@ -201,6 +201,27 @@ in the form `Hash<KeyType, ValueType>`, or using the hash specific syntax:
|
|
|
201
201
|
`Hash{KeyTypes=>ValueTypes}`. In the latter case, KeyTypes or ValueTypes can
|
|
202
202
|
also be a list of types separated by commas.
|
|
203
203
|
|
|
204
|
+
For more precise type signatures, the hash-specific syntax also supports
|
|
205
|
+
multiple keys mapping to the same value type(s), multiple key/value groups,
|
|
206
|
+
and nested hashes:
|
|
207
|
+
|
|
208
|
+
* **Multiple keys for the same value type(s)** — A comma-separated list of
|
|
209
|
+
keys on the left of `=>` all share the value types on the right. For example,
|
|
210
|
+
`Hash{:name, :title => String}` describes a Hash where both `:name` and
|
|
211
|
+
`:title` keys map to String values.
|
|
212
|
+
* **Multiple key/value groups** — Separate distinct key/value groups with
|
|
213
|
+
a semicolon (`;`). For example,
|
|
214
|
+
`Hash{:name => String; :age => Integer}` describes a Hash with a `:name` key
|
|
215
|
+
that maps to a String and an `:age` key that maps to an Integer.
|
|
216
|
+
* **Nested hashes** — A value type can itself be a hash, allowing nested
|
|
217
|
+
structures. For example,
|
|
218
|
+
`Hash{:user => Hash{:name => String, :age => Integer}}` describes a Hash with
|
|
219
|
+
a `:user` key whose value is another Hash with `:name` and `:age` keys.
|
|
220
|
+
|
|
221
|
+
Keys in the hash-specific syntax are commonly [literal values](#Literals) such
|
|
222
|
+
as symbols (`:key`) or strings (`'key'`, `"key"`), but any type listed in the
|
|
223
|
+
[type conventions](#Type_List_Conventions) is allowed.
|
|
224
|
+
|
|
204
225
|
#### Order-Dependent Lists
|
|
205
226
|
|
|
206
227
|
An order dependent list is a set of types surrounded by "()" and separated by
|
|
@@ -213,7 +234,7 @@ having exactly those 3 elements) would be listed as: `Array(String, Fixnum, Hash
|
|
|
213
234
|
Some literals are accepted by virtue of being Ruby literals, but also by YARD
|
|
214
235
|
conventions. Here is a non-exhaustive list of certain accepted literal values:
|
|
215
236
|
|
|
216
|
-
* `true`, `false`, `nil`, `:foo` — used when a method returns
|
|
237
|
+
* `true`, `false`, `nil`, `:foo`, `"bar"` — used when a method returns
|
|
217
238
|
these explicit literal values. Note that if your method returns both
|
|
218
239
|
`true` or `false`, you should use the `Boolean` conventional type
|
|
219
240
|
instead.
|
data/lib/yard/autoload.rb
CHANGED
|
@@ -287,6 +287,7 @@ module YARD
|
|
|
287
287
|
module Templates
|
|
288
288
|
module Helpers # Namespace for template helpers
|
|
289
289
|
module Markup # Namespace for markup providers
|
|
290
|
+
autoload :HtmlEntities, __p('templates/helpers/markup/html_entities')
|
|
290
291
|
autoload :HybridMarkdown, __p('templates/helpers/markup/hybrid_markdown')
|
|
291
292
|
autoload :RDocMarkup, __p('templates/helpers/markup/rdoc_markup')
|
|
292
293
|
autoload :RDocMarkdown, __p('templates/helpers/markup/rdoc_markdown')
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
require 'fileutils'
|
|
3
|
-
|
|
4
2
|
module YARD
|
|
5
3
|
module Server
|
|
6
4
|
module Commands
|
|
@@ -32,6 +30,8 @@ module YARD
|
|
|
32
30
|
# @abstract
|
|
33
31
|
# @see #run
|
|
34
32
|
class Base
|
|
33
|
+
include StaticCaching
|
|
34
|
+
|
|
35
35
|
# @group Basic Command and Adapter Options
|
|
36
36
|
|
|
37
37
|
# @return [Hash] the options passed to the command's constructor
|
|
@@ -163,13 +163,7 @@ module YARD
|
|
|
163
163
|
# @return [String] the same cached data (for chaining)
|
|
164
164
|
# @see StaticCaching
|
|
165
165
|
def cache(data)
|
|
166
|
-
if caching
|
|
167
|
-
path = File.join(adapter.document_root, request.path_info.sub(/\.html$/, '') + '.html')
|
|
168
|
-
path = path.sub(%r{/\.html$}, '.html')
|
|
169
|
-
FileUtils.mkdir_p(File.dirname(path))
|
|
170
|
-
log.debug "Caching data to #{path}"
|
|
171
|
-
File.open(path, 'wb') {|f| f.write(data) }
|
|
172
|
-
end
|
|
166
|
+
super if caching
|
|
173
167
|
self.body = data
|
|
174
168
|
end
|
|
175
169
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
|
|
2
4
|
module YARD
|
|
3
5
|
module Server
|
|
4
6
|
# Implements static caching for requests.
|
|
@@ -10,9 +12,8 @@ module YARD
|
|
|
10
12
|
# implement your own +#check_static_cache+ method and mix the module into
|
|
11
13
|
# the Router class.
|
|
12
14
|
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# {Commands::Base#cache}.
|
|
15
|
+
# This method checks for the existence of cached data. To actually cache
|
|
16
|
+
# a response, see {#cache}.
|
|
16
17
|
#
|
|
17
18
|
# @example Implementing In-Memory Cache Checking
|
|
18
19
|
# module MemoryCaching
|
|
@@ -33,14 +34,44 @@ module YARD
|
|
|
33
34
|
# @see Commands::Base#cache
|
|
34
35
|
def check_static_cache
|
|
35
36
|
return nil unless adapter.document_root
|
|
36
|
-
cache_path =
|
|
37
|
-
|
|
37
|
+
cache_path = cache_path(request.path)
|
|
38
|
+
return nil unless cache_path
|
|
39
|
+
|
|
38
40
|
if File.file?(cache_path)
|
|
39
41
|
log.debug "Loading cache from disk: #{cache_path}"
|
|
40
42
|
return [200, {'Content-Type' => 'text/html'}, [File.read_binary(cache_path)]]
|
|
41
43
|
end
|
|
42
44
|
nil
|
|
43
45
|
end
|
|
46
|
+
|
|
47
|
+
# Caches rendered HTML response data to disk.
|
|
48
|
+
#
|
|
49
|
+
# @param [String] data the data to cache
|
|
50
|
+
# @return [void]
|
|
51
|
+
# @since 0.9.44
|
|
52
|
+
def cache(data)
|
|
53
|
+
return unless adapter.document_root
|
|
54
|
+
|
|
55
|
+
path = cache_path(request.path_info)
|
|
56
|
+
return unless path
|
|
57
|
+
|
|
58
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
59
|
+
log.debug "Caching data to #{path}"
|
|
60
|
+
File.open(path, 'wb') {|f| f.write(data) }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def cache_path(request_path)
|
|
66
|
+
return nil if request_path.split(/[\/\\]/).include?('..')
|
|
67
|
+
|
|
68
|
+
path = request_path.sub(/\.html$/, '') + '.html'
|
|
69
|
+
path = path.sub(%r{\A/+}, '')
|
|
70
|
+
return nil if path =~ /\A[A-Za-z]:/
|
|
71
|
+
|
|
72
|
+
path = File.cleanpath(path)
|
|
73
|
+
File.join(adapter.document_root, path)
|
|
74
|
+
end
|
|
44
75
|
end
|
|
45
76
|
end
|
|
46
77
|
end
|
|
@@ -4,6 +4,9 @@ require 'strscan'
|
|
|
4
4
|
module YARD
|
|
5
5
|
module Tags
|
|
6
6
|
class TypesExplainer
|
|
7
|
+
# Regular expression to match symbol and string literals
|
|
8
|
+
LITERALMATCH = /:\w+|'[^']*'|"[^"]*"/
|
|
9
|
+
|
|
7
10
|
# (see Tag#explain_types)
|
|
8
11
|
# @param types [Array<String>] a list of types to parse and summarize
|
|
9
12
|
def self.explain(*types)
|
|
@@ -31,16 +34,14 @@ module YARD
|
|
|
31
34
|
end
|
|
32
35
|
|
|
33
36
|
def to_s(singular = true)
|
|
34
|
-
if name[0, 1]
|
|
35
|
-
(singular ? "an object that responds to " : "objects that respond to ") + list_join(name.split(/ *& */), with: "and")
|
|
36
|
-
elsif name[0, 1] =~ /[A-Z]/
|
|
37
|
+
if name[0, 1] =~ /[A-Z]/
|
|
37
38
|
singular ? "a#{name[0, 1] =~ /[aeiou]/i ? 'n' : ''} " + name : "#{name}#{name[-1, 1] =~ /[A-Z]/ ? "'" : ''}s"
|
|
38
39
|
else
|
|
39
40
|
name
|
|
40
41
|
end
|
|
41
42
|
end
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
protected
|
|
44
45
|
|
|
45
46
|
def list_join(list, with: "or")
|
|
46
47
|
index = 0
|
|
@@ -54,6 +55,20 @@ module YARD
|
|
|
54
55
|
end
|
|
55
56
|
end
|
|
56
57
|
|
|
58
|
+
# @private
|
|
59
|
+
class LiteralType < Type
|
|
60
|
+
def to_s(_singular = true)
|
|
61
|
+
"a literal value #{name}"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @private
|
|
66
|
+
class DuckType < Type
|
|
67
|
+
def to_s(singular = true)
|
|
68
|
+
(singular ? "an object that responds to " : "objects that respond to ") + list_join(name.split(/ *& */), with: "and")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
57
72
|
# @private
|
|
58
73
|
class CollectionType < Type
|
|
59
74
|
attr_accessor :types
|
|
@@ -77,18 +92,56 @@ module YARD
|
|
|
77
92
|
|
|
78
93
|
# @private
|
|
79
94
|
class HashCollectionType < Type
|
|
80
|
-
attr_accessor :
|
|
95
|
+
attr_accessor :key_value_pairs
|
|
81
96
|
|
|
82
|
-
def initialize(name,
|
|
97
|
+
def initialize(name, key_types_or_pairs, value_types = nil)
|
|
83
98
|
@name = name
|
|
84
|
-
|
|
85
|
-
|
|
99
|
+
|
|
100
|
+
if value_types.nil?
|
|
101
|
+
# New signature: (name, key_value_pairs)
|
|
102
|
+
@key_value_pairs = key_types_or_pairs || []
|
|
103
|
+
else
|
|
104
|
+
# Old signature: (name, key_types, value_types)
|
|
105
|
+
@key_value_pairs = [[key_types_or_pairs, value_types]]
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Backward compatibility accessors
|
|
110
|
+
def key_types
|
|
111
|
+
return [] if @key_value_pairs.empty?
|
|
112
|
+
@key_value_pairs.first[0] || []
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def key_types=(types)
|
|
116
|
+
if @key_value_pairs.empty?
|
|
117
|
+
@key_value_pairs = [[types, []]]
|
|
118
|
+
else
|
|
119
|
+
@key_value_pairs[0][0] = types
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def value_types
|
|
124
|
+
return [] if @key_value_pairs.empty?
|
|
125
|
+
@key_value_pairs.first[1] || []
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def value_types=(types)
|
|
129
|
+
if @key_value_pairs.empty?
|
|
130
|
+
@key_value_pairs = [[[], types]]
|
|
131
|
+
else
|
|
132
|
+
@key_value_pairs[0][1] = types
|
|
133
|
+
end
|
|
86
134
|
end
|
|
87
135
|
|
|
88
136
|
def to_s(_singular = true)
|
|
89
|
-
"a#{name[0, 1] =~ /[aeiou]/i ? 'n' : ''} #{name}
|
|
90
|
-
|
|
91
|
-
|
|
137
|
+
return "a#{name[0, 1] =~ /[aeiou]/i ? 'n' : ''} #{name}" if @key_value_pairs.empty?
|
|
138
|
+
|
|
139
|
+
result = "a#{name[0, 1] =~ /[aeiou]/i ? 'n' : ''} #{name} with "
|
|
140
|
+
parts = @key_value_pairs.map do |keys, values|
|
|
141
|
+
"keys made of (" + list_join(keys.map {|t| t.to_s(false) }) +
|
|
142
|
+
") and values of (" + list_join(values.map {|t| t.to_s(false) }) + ")"
|
|
143
|
+
end
|
|
144
|
+
result + parts.join(" and ")
|
|
92
145
|
end
|
|
93
146
|
end
|
|
94
147
|
|
|
@@ -101,13 +154,15 @@ module YARD
|
|
|
101
154
|
:collection_end => />/,
|
|
102
155
|
:fixed_collection_start => /\(/,
|
|
103
156
|
:fixed_collection_end => /\)/,
|
|
104
|
-
:type_name => /#{ISEP}#{METHODNAMEMATCH}|#{NAMESPACEMATCH}|\w+/,
|
|
157
|
+
:type_name => /#{ISEP}#{METHODNAMEMATCH}|#{NAMESPACEMATCH}|#{LITERALMATCH}|\w+/,
|
|
105
158
|
:symbol => /:#{METHODNAMEMATCH}/,
|
|
106
|
-
:type_next => /[
|
|
159
|
+
:type_next => /[,]/,
|
|
107
160
|
:whitespace => /\s+/,
|
|
108
161
|
:hash_collection_start => /\{/,
|
|
109
|
-
:
|
|
162
|
+
:hash_collection_value => /=>/,
|
|
163
|
+
:hash_collection_value_end => /;/,
|
|
110
164
|
:hash_collection_end => /\}/,
|
|
165
|
+
# :symbol_start => /:/,
|
|
111
166
|
:parse_end => nil
|
|
112
167
|
}
|
|
113
168
|
|
|
@@ -119,10 +174,45 @@ module YARD
|
|
|
119
174
|
@scanner = StringScanner.new(string)
|
|
120
175
|
end
|
|
121
176
|
|
|
122
|
-
|
|
123
|
-
|
|
177
|
+
# @return [Array(Boolean, Array<Type>)] - finished, types
|
|
178
|
+
def parse(until_tokens: [:parse_end])
|
|
179
|
+
current_parsed_types = []
|
|
124
180
|
type = nil
|
|
125
181
|
name = nil
|
|
182
|
+
finished = false
|
|
183
|
+
parse_with_handlers do |token_type, token|
|
|
184
|
+
case token_type
|
|
185
|
+
when *until_tokens
|
|
186
|
+
raise SyntaxError, "expecting name, got '#{token}'" if name.nil?
|
|
187
|
+
type = create_type(name) unless type
|
|
188
|
+
current_parsed_types << type
|
|
189
|
+
finished = true
|
|
190
|
+
when :type_name
|
|
191
|
+
raise SyntaxError, "expecting END, got name '#{token}'" if name
|
|
192
|
+
name = token
|
|
193
|
+
when :type_next
|
|
194
|
+
raise SyntaxError, "expecting name, got '#{token}' at #{@scanner.pos}" if name.nil?
|
|
195
|
+
type = create_type(name) unless type
|
|
196
|
+
current_parsed_types << type
|
|
197
|
+
name = nil
|
|
198
|
+
type = nil
|
|
199
|
+
when :fixed_collection_start, :collection_start
|
|
200
|
+
name ||= "Array"
|
|
201
|
+
klass = token_type == :collection_start ? CollectionType : FixedCollectionType
|
|
202
|
+
type = klass.new(name, parse(until_tokens: [:fixed_collection_end, :collection_end, :parse_end]))
|
|
203
|
+
when :hash_collection_start
|
|
204
|
+
name ||= "Hash"
|
|
205
|
+
type = parse_hash_collection(name)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
[finished, current_parsed_types]
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
private
|
|
213
|
+
|
|
214
|
+
# @return [Array<Type>]
|
|
215
|
+
def parse_with_handlers
|
|
126
216
|
loop do
|
|
127
217
|
found = false
|
|
128
218
|
TOKENS.each do |token_type, match|
|
|
@@ -130,32 +220,67 @@ module YARD
|
|
|
130
220
|
# rubocop:disable Lint/AssignmentInCondition
|
|
131
221
|
next unless (match.nil? && @scanner.eos?) || (match && token = @scanner.scan(match))
|
|
132
222
|
found = true
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
when :type_next
|
|
138
|
-
raise SyntaxError, "expecting name, got '#{token}' at #{@scanner.pos}" if name.nil?
|
|
139
|
-
type = Type.new(name) unless type
|
|
140
|
-
types << type
|
|
141
|
-
type = nil
|
|
142
|
-
name = nil
|
|
143
|
-
when :fixed_collection_start, :collection_start
|
|
144
|
-
name ||= "Array"
|
|
145
|
-
klass = token_type == :collection_start ? CollectionType : FixedCollectionType
|
|
146
|
-
type = klass.new(name, parse)
|
|
147
|
-
when :hash_collection_start
|
|
148
|
-
name ||= "Hash"
|
|
149
|
-
type = HashCollectionType.new(name, parse, parse)
|
|
150
|
-
when :hash_collection_next, :hash_collection_end, :fixed_collection_end, :collection_end, :parse_end
|
|
151
|
-
raise SyntaxError, "expecting name, got '#{token}'" if name.nil?
|
|
152
|
-
type = Type.new(name) unless type
|
|
153
|
-
types << type
|
|
154
|
-
return types
|
|
155
|
-
end
|
|
223
|
+
# @type [Array<Type>]
|
|
224
|
+
finished, types = yield(token_type, token)
|
|
225
|
+
return types if finished
|
|
226
|
+
break
|
|
156
227
|
end
|
|
157
228
|
raise SyntaxError, "invalid character at #{@scanner.peek(1)}" unless found
|
|
158
229
|
end
|
|
230
|
+
nil
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def parse_hash_collection(name)
|
|
234
|
+
key_value_pairs = []
|
|
235
|
+
current_keys = []
|
|
236
|
+
finished = false
|
|
237
|
+
|
|
238
|
+
parse_with_handlers do |token_type, token|
|
|
239
|
+
case token_type
|
|
240
|
+
when :type_name
|
|
241
|
+
current_keys << create_type(token)
|
|
242
|
+
when :type_next
|
|
243
|
+
# Comma - continue collecting keys unless we just processed a value
|
|
244
|
+
# In that case, start a new key group
|
|
245
|
+
when :hash_collection_value
|
|
246
|
+
# => - current keys map to the next value(s)
|
|
247
|
+
raise SyntaxError, "no keys before =>" if current_keys.empty?
|
|
248
|
+
values = parse(until_tokens: [:hash_collection_value_end, :parse_end])
|
|
249
|
+
key_value_pairs << [current_keys, values]
|
|
250
|
+
current_keys = []
|
|
251
|
+
when :hash_collection_end, :parse_end
|
|
252
|
+
# End of hash
|
|
253
|
+
finished = true
|
|
254
|
+
when :whitespace
|
|
255
|
+
# Ignore whitespace
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
[finished, HashCollectionType.new(name, key_value_pairs)]
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
private
|
|
263
|
+
|
|
264
|
+
def create_type(name)
|
|
265
|
+
if name[0, 1] == ":" || (name[0, 1] =~ /['"]/ && name[-1, 1] =~ /['"]/)
|
|
266
|
+
LiteralType.new(name)
|
|
267
|
+
elsif name[0, 1] == "#"
|
|
268
|
+
DuckType.new(name)
|
|
269
|
+
else
|
|
270
|
+
Type.new(name)
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
private
|
|
275
|
+
|
|
276
|
+
def create_type(name)
|
|
277
|
+
if name[0, 1] == ":" || (name[0, 1] =~ /['"]/ && name[-1, 1] =~ /['"]/)
|
|
278
|
+
LiteralType.new(name)
|
|
279
|
+
elsif name[0, 1] == "#"
|
|
280
|
+
DuckType.new(name)
|
|
281
|
+
else
|
|
282
|
+
Type.new(name)
|
|
283
|
+
end
|
|
159
284
|
end
|
|
160
285
|
end
|
|
161
286
|
end
|