rbs 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +7 -1
  3. data/.gitignore +1 -1
  4. data/CHANGELOG.md +7 -0
  5. data/Gemfile +12 -0
  6. data/README.md +86 -47
  7. data/Rakefile +54 -21
  8. data/bin/rbs-prof +9 -0
  9. data/bin/run_in_md.rb +49 -0
  10. data/lib/rbs.rb +2 -0
  11. data/lib/rbs/ast/declarations.rb +62 -7
  12. data/lib/rbs/ast/members.rb +41 -17
  13. data/lib/rbs/cli.rb +299 -121
  14. data/lib/rbs/constant.rb +4 -4
  15. data/lib/rbs/constant_table.rb +50 -44
  16. data/lib/rbs/definition.rb +175 -59
  17. data/lib/rbs/definition_builder.rb +647 -603
  18. data/lib/rbs/environment.rb +338 -209
  19. data/lib/rbs/environment_walker.rb +14 -23
  20. data/lib/rbs/errors.rb +141 -3
  21. data/lib/rbs/parser.y +14 -9
  22. data/lib/rbs/prototype/rb.rb +100 -112
  23. data/lib/rbs/prototype/rbi.rb +4 -2
  24. data/lib/rbs/prototype/runtime.rb +10 -6
  25. data/lib/rbs/substitution.rb +8 -1
  26. data/lib/rbs/test/hook.rb +2 -2
  27. data/lib/rbs/test/setup.rb +3 -1
  28. data/lib/rbs/test/test_helper.rb +2 -5
  29. data/lib/rbs/test/type_check.rb +1 -2
  30. data/lib/rbs/type_name_resolver.rb +58 -0
  31. data/lib/rbs/types.rb +94 -2
  32. data/lib/rbs/validator.rb +51 -0
  33. data/lib/rbs/variance_calculator.rb +12 -2
  34. data/lib/rbs/version.rb +1 -1
  35. data/lib/rbs/writer.rb +125 -89
  36. data/rbs.gemspec +0 -10
  37. data/schema/decls.json +15 -0
  38. data/schema/members.json +3 -0
  39. data/stdlib/benchmark/benchmark.rbs +151 -151
  40. data/stdlib/builtin/enumerable.rbs +1 -1
  41. data/stdlib/builtin/file.rbs +0 -3
  42. data/stdlib/builtin/io.rbs +4 -4
  43. data/stdlib/builtin/thread.rbs +2 -2
  44. data/stdlib/csv/csv.rbs +4 -6
  45. data/stdlib/fiber/fiber.rbs +1 -1
  46. data/stdlib/json/json.rbs +1 -1
  47. data/stdlib/mutex_m/mutex_m.rbs +77 -0
  48. data/stdlib/pathname/pathname.rbs +6 -6
  49. data/stdlib/prime/integer-extension.rbs +1 -1
  50. data/stdlib/prime/prime.rbs +44 -44
  51. data/stdlib/tmpdir/tmpdir.rbs +1 -1
  52. metadata +8 -129
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 77a83be097e5de26f4541a25fb744a31574d0f0b94d94848e11cae49d095693a
4
- data.tar.gz: 4619e6ae9f31e32ac6d2da902b11980644df037b9d254b5919fe2eff1f43bdc7
3
+ metadata.gz: a5318ae7911a3e7e4e1bed761e0c7b8d1fc248e698b0d53f8a39d388da5617f0
4
+ data.tar.gz: d90f20d41372bcc852f49b844aa6b6b5367d9d88ec686a13ab00841f1cf1e2ed
5
5
  SHA512:
6
- metadata.gz: '095abed7b0453d72a545760d8a165bb2a3415e3f741b004926b600b8e1e1260dc752e91d86d33e0c770d3011885bea1fa728708707687c59d3a1ad9707b672c7'
7
- data.tar.gz: 94b3f4fc317d701c327b7519068ec590409ceff1208660c44f83440c9d3ed419c83ca29adf7137c10a1fb7c3d7ed7b0808e0a8a5fc5dedd66ca900981674ae90
6
+ metadata.gz: 702ae082267eb6a087e19c5a37a83aaaeea0f76e83eba7cc984a15cca869745d1b934231bf6910dbbba972a06301948eca740aa2a8fc49c378487a030befe909
7
+ data.tar.gz: 8f467c52d50c25886965e1762f7c52f246cb90fa313c7d0a9400051f32c7f3603ada0142cf90ebfad4b56ccbaf57bacd5d1467cb8daf20247254c90937b9c599
@@ -24,5 +24,11 @@ jobs:
24
24
  ruby -v
25
25
  gem install bundler
26
26
  bin/setup
27
- bundle exec rake
27
+ bundle exec rake test
28
+ bundle exec rake stdlib_test
29
+ bundle exec rake rubocop
30
+ bundle exec rake validate
31
+ bundle exec rake test_doc
28
32
  bundle exec rake build
33
+ RBS_GENERATE_TEST_PATH=/tmp/Array_test.rb bundle exec rake 'generate:stdlib_test[Array]'
34
+ ruby -c /tmp/Array_test.rb
data/.gitignore CHANGED
@@ -5,8 +5,8 @@
5
5
  /pkg/
6
6
  /spec/reports/
7
7
  /tmp/
8
- /Gemfile.lock
9
8
  /.ruby-version
10
9
  /lib/rbs/parser.rb
11
10
  /lib/rbs/parser.output
12
11
  /vendor/sigs
12
+ /Gemfile.lock
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.5.0 (2020-07-04)
6
+
7
+ * Signature updates for `Mutex_m`, `IO`, and `Enumerable`.
8
+ * Syntax update. [#307](https://github.com/ruby/rbs/pull/307)
9
+ * AST command prints _absolute_ type names with file name filtering. [#312](https://github.com/ruby/rbs/pull/312)
10
+ * Improve CLI message. [#309](https://github.com/ruby/rbs/pull/309)
11
+
5
12
  ## 0.4.0 (2020-06-15)
6
13
 
7
14
  * Signature update for `Fiber`, `Encoding`, and `Enumerator`.
data/Gemfile CHANGED
@@ -3,4 +3,16 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in rbs.gemspec
4
4
  gemspec
5
5
 
6
+ # Development dependencies
7
+ gem "rake"
8
+ gem "minitest"
9
+ gem "racc"
10
+ gem "rubocop"
11
+ gem "rubocop-rubycw"
12
+ gem "minitest-reporters"
13
+ gem "json"
14
+ gem "json-schema"
15
+ gem 'stackprof'
16
+
17
+ # Test gems
6
18
  gem "rbs-amber", path: "test/assets/test-gem"
data/README.md CHANGED
@@ -1,79 +1,118 @@
1
1
  # RBS
2
2
 
3
- RBS provides syntax and semantics definition for the `Ruby Signature` language, `.rbs` files.
4
- It consists of a parser, the syntax, and class definition interpreter, the semantics.
3
+ RBS is a language to describe the structure of Ruby programs.
4
+ You can write down the definition of a class or module: methods defined in the class, instance variables and their types, and inheritance/mix-in relations.
5
+ It also allows declaring constants and global variables.
5
6
 
6
- ## Build
7
+ The following is a small example of RBS for a chat app.
7
8
 
8
- We haven't published a gem yet.
9
- You need to install the dependencies, and build its parser with `bin/setup`.
9
+ <!-- run-start:a.rbs:bundle exec rbs -I a.rbs validate -->
10
+ ```rbs
11
+ module ChatApp
12
+ VERSION: String
10
13
 
11
- ```
12
- $ bin/setup
13
- $ bundle exec exe/rbs
14
- ```
14
+ class User
15
+ attr_reader login: String
16
+ attr_reader email: String
15
17
 
16
- ## Usage
18
+ def initialize: (login: String, email: String) -> void
19
+ end
17
20
 
18
- ```
19
- $ rbs list
20
- $ rbs ancestors ::Object
21
- $ rbs methods ::Object
22
- $ rbs method ::Object tap
23
- ```
21
+ class Bot
22
+ attr_reader name: String
23
+ attr_reader email: String
24
+ attr_reader owner: User
24
25
 
25
- ### rbs list [--class|--module|--interface]
26
+ def initialize: (name: String, owner: User) -> void
27
+ end
26
28
 
27
- ```
28
- $ rbs list
29
- ```
29
+ class Message
30
+ attr_reader id: String
31
+ attr_reader string: String
32
+ attr_reader from: User | Bot # `|` means union types: `#from` can be `User` or `Bot`
33
+ attr_reader reply_to: Message? # `?` means optional type: `#reply_to` can be `nil`
30
34
 
31
- This command lists all of the classes/modules/interfaces defined in `.rbs` files.
35
+ def initialize: (from: User | Bot, string: String) -> void
32
36
 
33
- ### rbs ancestors [--singleton|--instance] CLASS
37
+ def reply: (from: User | Bot, string: String) -> Message
38
+ end
34
39
 
40
+ class Channel
41
+ attr_reader name: String
42
+ attr_reader messages: Array[Message]
43
+ attr_reader users: Array[User]
44
+ attr_reader bots: Array[Bot]
45
+
46
+ def initialize: (name: String) -> void
47
+
48
+ def each_member: () { (User | Bot) -> void } -> void # `{` and `}` means block.
49
+ | () -> Enumerable[User | Bot, void] # Method can be overloaded.
50
+ end
51
+ end
35
52
  ```
36
- $ rbs ancestors Array # ([].class.ancestors)
37
- $ rbs ancestors --singleton Array # (Array.class.ancestors)
38
- ```
53
+ <!-- run-end -->
39
54
 
40
- This command prints the _ancestors_ of the class.
41
- The name of the command is borrowed from `Class#ancestors`, but the semantics is a bit different.
42
- The `ancestors` command is more precise (I believe).
55
+ ## Installation
43
56
 
44
- ### rbs methods [--singleton|--instance] CLASS
57
+ Install the `rbs` gem. `$ gem install rbs` from the command line, or add a line in your `Gemfile`.
45
58
 
46
- ```
47
- $ rbs methods ::Integer # 1.methods
48
- $ rbs methods --singleton ::Object # Object.methods
59
+ ```rb
60
+ gem "rbs"
49
61
  ```
50
62
 
51
- This command prints all methods provided for the class.
63
+ ## CLI
52
64
 
53
- ### rbs method [--singleton|--instance] CLASS METHOD
65
+ The gem ships with the `rbs` command line tool to demonstrate what it can do and help develop RBS.
54
66
 
55
- ```
56
- $ rbs method ::Integer '+' # 1+2
57
- $ rbs method --singleton ::Object tap # Object.tap { ... }
67
+ ```bash
68
+ $ rbs version
69
+ $ rbs list
70
+ $ rbs ancestors ::Object
71
+ $ rbs methods ::Object
72
+ $ rbs method Object then
58
73
  ```
59
74
 
60
- This command prints type and properties of the method.
75
+ ## Library
61
76
 
62
- ### Options
77
+ There are two important concepts, _environment_ and _definition_.
63
78
 
64
- It accepts two global options, `-r` and `-I`.
79
+ An _environment_ is a dictionary that keeps track of all declarations. What is the declaration associated with `String` class? An _environment_ will give you the answer.
65
80
 
66
- `-r` is for libraries. You can specify the names of libraries.
81
+ A _definition_ gives you the detail of the class. What is the type of the return value of `gsub` method of the `String` class? The _definition_ for `String` class knows the list of methods it provides and their types.
67
82
 
68
- ```
69
- $ rbs -r set list
70
- ```
83
+ The following is a small code to retrieve the definition of the `String#gsub` method.
71
84
 
72
- `-I` is for application signatures. You can specify the name of directory.
85
+ <!-- run-start:a.rb:bundle exec ruby a.rb -->
86
+ ```rb
87
+ require "rbs"
73
88
 
89
+ loader = RBS::EnvironmentLoader.new()
90
+
91
+ # loader.add(path: Pathname("sig")) # Load .rbs files from `sig` directory
92
+ # loader.add(library: "pathname") # Load pathname library
93
+
94
+ environment = RBS::Environment.from_loader(loader).resolve_type_names
95
+
96
+ # ::String
97
+ string = RBS::TypeName.new(name: :String, namespace: RBS::Namespace.root)
98
+
99
+ # Class declaration for ::String
100
+ decl = environment.class_decls[string]
101
+
102
+ # Builder provides the translation from `declaration` to `definition`
103
+ builder = RBS::DefinitionBuilder.new(env: environment)
104
+
105
+ # Definition of instance of String
106
+ instance = builder.build_instance(string)
107
+ # Print the types of `gsub` method
108
+ puts instance.methods[:gsub].method_types.join("\n")
109
+
110
+ # Definition of singleton of String
111
+ singleton = builder.build_singleton(string)
112
+ # No `gsub` method for String singleton
113
+ puts singleton.methods[:gsub]
74
114
  ```
75
- $ rbs -I sig list
76
- ```
115
+ <!-- run-end -->
77
116
 
78
117
  ## Guides
79
118
 
data/Rakefile CHANGED
@@ -9,10 +9,23 @@ Rake::TestTask.new(:test) do |t|
9
9
  end
10
10
  end
11
11
 
12
- multitask :default => [:test, :stdlib_test, :rubocop, :validate]
12
+ multitask :default => [:test, :stdlib_test, :rubocop, :validate, :test_doc]
13
+
14
+ task :test_doc => :parser do
15
+ files = Dir.chdir(File.expand_path('..', __FILE__)) do
16
+ `git ls-files -z`.split("\x0").select do |file| Pathname(file).extname == ".md" end
17
+ end
18
+
19
+ sh "ruby bin/run_in_md.rb #{files.join(" ")}"
20
+ end
13
21
 
14
22
  task :validate => :parser do
15
23
  sh "rbs validate"
24
+
25
+ FileList["stdlib/*"].each do |path|
26
+ next if path =~ %r{stdlib/builtin}
27
+ sh "rbs -r#{File.basename(path)} validate"
28
+ end
16
29
  end
17
30
 
18
31
  FileList["test/stdlib/**/*_test.rb"].each do |test|
@@ -41,7 +54,7 @@ namespace :generate do
41
54
  raise "Class name is necessary. e.g. rake 'generate:stdlib_test[String]'"
42
55
  end
43
56
 
44
- path = Pathname("test/stdlib/#{klass}_test.rb")
57
+ path = Pathname(ENV["RBS_GENERATE_TEST_PATH"] || "test/stdlib/#{klass}_test.rb")
45
58
  raise "#{path} already exists!" if path.exist?
46
59
 
47
60
  require "erb"
@@ -53,35 +66,55 @@ namespace :generate do
53
66
  def initialize(klass)
54
67
  @klass = klass
55
68
 
56
- @env = Ruby::Signature::Environment.new
57
- Ruby::Signature::EnvironmentLoader.new.load(env: @env)
69
+ loader = RBS::EnvironmentLoader.new
70
+ Dir['stdlib/*'].each do |lib|
71
+ next if lib.end_with?('builtin')
72
+
73
+ loader.add(library: File.basename(lib))
74
+ end
75
+ @env = RBS::Environment.from_loader(loader).resolve_type_names
58
76
  end
59
77
 
60
78
  def call
61
79
  ERB.new(<<~ERB, trim_mode: "-").result(binding)
62
80
  require_relative "test_helper"
81
+ require 'rbs/test/test_helper'
82
+
83
+ <%- unless class_methods.empty? -%>
84
+ class <%= klass %>SingletonTest < Minitest::Test
85
+ include RBS::Test::TypeAssertions
63
86
 
64
- class <%= klass %>Test < StdlibTest
65
- target <%= klass %>
66
87
  # library "pathname", "set", "securerandom" # Declare library signatures to load
67
- using hook.refinement
88
+ testing "singleton(::<%= klass %>)"
89
+
68
90
  <%- class_methods.each do |method_name, definition| %>
69
- def test_class_method_<%= test_name_for(method_name) %>
70
- <%- definition.method_types.each do |method_type| -%>
71
- # <%= method_type %>
72
- <%= klass %>.<%= method_name %>
73
- <%- end -%>
91
+ def test_<%= test_name_for(method_name) %>
92
+ <%- definition.method_types.each do |method_type| -%>
93
+ assert_send_type "<%= method_type %>",
94
+ <%= klass %>, :<%= method_name %>
95
+ <%- end -%>
74
96
  end
75
97
  <%- end -%>
98
+ end
99
+ <%- end -%>
100
+
101
+ <%- unless instance_methods.empty? -%>
102
+ class <%= klass %>Test < Minitest::Test
103
+ include RBS::Test::TypeAssertions
104
+
105
+ # library "pathname", "set", "securerandom" # Declare library signatures to load
106
+ testing "::<%= klass %>"
107
+
76
108
  <%- instance_methods.each do |method_name, definition| %>
77
109
  def test_<%= test_name_for(method_name) %>
78
- <%- definition.method_types.each do |method_type| -%>
79
- # <%= method_type %>
80
- <%= klass %>.new.<%= method_name %>
81
- <%- end -%>
110
+ <%- definition.method_types.each do |method_type| -%>
111
+ assert_send_type "<%= method_type %>",
112
+ <%= klass %>.new, :<%= method_name %>
113
+ <%- end -%>
82
114
  end
83
115
  <%- end -%>
84
116
  end
117
+ <%- end -%>
85
118
  ERB
86
119
  end
87
120
 
@@ -117,18 +150,18 @@ namespace :generate do
117
150
  end
118
151
 
119
152
  def type_name
120
- @type_name ||= Ruby::Signature::TypeName.new(name: klass.to_sym, namespace: Ruby::Signature::Namespace.new(path: [], absolute: true))
153
+ @type_name ||= RBS::TypeName.new(name: klass.to_sym, namespace: RBS::Namespace.new(path: [], absolute: true))
121
154
  end
122
155
 
123
156
  def class_methods
124
- @class_methods ||= Ruby::Signature::DefinitionBuilder.new(env: env).build_singleton(type_name).methods.select {|_, definition|
125
- definition.implemented_in.name.absolute! == type_name
157
+ @class_methods ||= RBS::DefinitionBuilder.new(env: env).build_singleton(type_name).methods.select {|_, definition|
158
+ definition.implemented_in == type_name
126
159
  }
127
160
  end
128
161
 
129
162
  def instance_methods
130
- @instance_methods ||= Ruby::Signature::DefinitionBuilder.new(env: env).build_instance(type_name).methods.select {|_, definition|
131
- definition.implemented_in.name.absolute! == type_name
163
+ @instance_methods ||= RBS::DefinitionBuilder.new(env: env).build_instance(type_name).methods.select {|_, definition|
164
+ definition.implemented_in == type_name
132
165
  }
133
166
  end
134
167
  end
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "stackprof"
4
+
5
+ out = ENV["RBS_STACKPROF_OUT"] || 'tmp/stackprof-cpu-rbs.dump'
6
+
7
+ StackProf.run(mode: :cpu, out: out) do
8
+ load File.join(__dir__, "../exe/rbs")
9
+ end
@@ -0,0 +1,49 @@
1
+ require "tmpdir"
2
+ require "shellwords"
3
+
4
+ ARGV.each do |path|
5
+ puts "~~~~~~~ Checking #{path} ~~~~~~"
6
+ content = File.read(path)
7
+
8
+ snippets = []
9
+ lines = []
10
+ content.lines.each.with_index do |line, index|
11
+ case line
12
+ when /run-start:/
13
+ lines = [[line, index+1]]
14
+ when /run-end/
15
+ lines << [line, index+1]
16
+ snippets << lines
17
+ lines = []
18
+ else
19
+ lines << [line, index+1]
20
+ end
21
+ end
22
+
23
+ snippets.each do |lines|
24
+ puts ">>> Code detected: "
25
+ hd, _, *code_lines, _, _ = lines
26
+
27
+ head = hd[0]
28
+ head.gsub!(/^<!-- +/, "").gsub!(/ +-->$/, "")
29
+ _,name,command = head.split(/:/)
30
+
31
+ puts "# command=#{Shellwords.split(command).inspect}"
32
+ puts "# name=#{name}"
33
+ puts code_lines.map {|line, i| "#{"%4d" % i} #{line}" }.join
34
+
35
+ code = code_lines.map(&:first).join
36
+
37
+ puts ">>> Running..."
38
+ Dir.mktmpdir do |dir|
39
+ File.write(File.join(dir, name), code)
40
+ pid = spawn({ "BUNDLE_GEMFILE" => File.join(__dir__, "../Gemfile") },
41
+ *Shellwords.split(command),
42
+ chdir: dir)
43
+
44
+ _, status = Process.waitpid2(pid)
45
+
46
+ status.success? or raise "Failed to execute code: #{code_lines.join}"
47
+ end
48
+ end
49
+ end
data/lib/rbs.rb CHANGED
@@ -32,8 +32,10 @@ require "rbs/writer"
32
32
  require "rbs/prototype/rbi"
33
33
  require "rbs/prototype/rb"
34
34
  require "rbs/prototype/runtime"
35
+ require "rbs/type_name_resolver"
35
36
  require "rbs/environment_walker"
36
37
  require "rbs/vendorer"
38
+ require "rbs/validator"
37
39
 
38
40
  begin
39
41
  require "rbs/parser"
@@ -1,6 +1,9 @@
1
1
  module RBS
2
2
  module AST
3
3
  module Declarations
4
+ class Base
5
+ end
6
+
4
7
  class ModuleTypeParams
5
8
  attr_reader :params
6
9
 
@@ -77,7 +80,53 @@ module RBS
77
80
  end
78
81
  end
79
82
 
80
- class Class
83
+ module NestedDeclarationHelper
84
+ def each_member
85
+ if block_given?
86
+ members.each do |member|
87
+ if member.is_a?(Members::Base)
88
+ yield member
89
+ end
90
+ end
91
+ else
92
+ enum_for :each_member
93
+ end
94
+ end
95
+
96
+ def each_decl
97
+ if block_given?
98
+ members.each do |member|
99
+ if member.is_a?(Declarations::Base)
100
+ yield member
101
+ end
102
+ end
103
+ else
104
+ enum_for :each_decl
105
+ end
106
+ end
107
+ end
108
+
109
+ module MixinHelper
110
+ def each_mixin(&block)
111
+ if block_given?
112
+ @mixins ||= begin
113
+ members.select do |member|
114
+ case member
115
+ when Members::Include, Members::Extend, Members::Prepend
116
+ true
117
+ else
118
+ false
119
+ end
120
+ end
121
+ end
122
+ @mixins.each(&block)
123
+ else
124
+ enum_for :each_mixin
125
+ end
126
+ end
127
+ end
128
+
129
+ class Class < Base
81
130
  class Super
82
131
  attr_reader :name
83
132
  attr_reader :args
@@ -105,6 +154,9 @@ module RBS
105
154
  end
106
155
  end
107
156
 
157
+ include NestedDeclarationHelper
158
+ include MixinHelper
159
+
108
160
  attr_reader :name
109
161
  attr_reader :type_params
110
162
  attr_reader :members
@@ -151,7 +203,10 @@ module RBS
151
203
  end
152
204
  end
153
205
 
154
- class Module
206
+ class Module < Base
207
+ include NestedDeclarationHelper
208
+ include MixinHelper
209
+
155
210
  attr_reader :name
156
211
  attr_reader :type_params
157
212
  attr_reader :members
@@ -198,7 +253,7 @@ module RBS
198
253
  end
199
254
  end
200
255
 
201
- class Extension
256
+ class Extension < Base
202
257
  attr_reader :name
203
258
  attr_reader :type_params
204
259
  attr_reader :extension_name
@@ -245,7 +300,7 @@ module RBS
245
300
  end
246
301
  end
247
302
 
248
- class Interface
303
+ class Interface < Base
249
304
  attr_reader :name
250
305
  attr_reader :type_params
251
306
  attr_reader :members
@@ -288,7 +343,7 @@ module RBS
288
343
  end
289
344
  end
290
345
 
291
- class Alias
346
+ class Alias < Base
292
347
  attr_reader :name
293
348
  attr_reader :type
294
349
  attr_reader :annotations
@@ -327,7 +382,7 @@ module RBS
327
382
  end
328
383
  end
329
384
 
330
- class Constant
385
+ class Constant < Base
331
386
  attr_reader :name
332
387
  attr_reader :type
333
388
  attr_reader :location
@@ -363,7 +418,7 @@ module RBS
363
418
  end
364
419
  end
365
420
 
366
- class Global
421
+ class Global < Base
367
422
  attr_reader :name
368
423
  attr_reader :type
369
424
  attr_reader :location