Skip to content

Commit 177ddd6

Browse files
committed
Add docs
1 parent 10d47d0 commit 177ddd6

File tree

3 files changed

+200
-0
lines changed

3 files changed

+200
-0
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ $ ruby-signature -r set list
7676
$ ruby-signature -I sig list
7777
```
7878

79+
## Guides
80+
81+
- [Writing signatures guide](doc/sigs.md)
82+
- [Stdlib signatures guide](doc/stdlib.md)
83+
- [Syntax](doc/syntax.md)
84+
7985
## Development
8086

8187
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.

doc/sigs.md

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Writing Signatures Guide
2+
3+
You can write the signature of your applications and libraries.
4+
Signature of your Ruby program would help:
5+
6+
1. Understanding the code structure
7+
2. Finding APIs
8+
9+
And if you ship your gem with signature, the gem users can type check their applications!
10+
11+
## Writing signatures
12+
13+
You first need to write your program's signature.
14+
See [syntax guide](syntax.md).
15+
16+
## Testing signatures
17+
18+
When you finish writing signature, you may want to test the signature.
19+
ruby-signature provides a feature to test your signature.
20+
21+
```
22+
$ RBS_TEST_TARGET='Foo::*' bundle exec ruby -r ruby/signature/test/setup test/foo_test.rb
23+
```
24+
25+
The test installs instrumentations to spy the method calls and check if arguments/return values are correct with respect to the type of the method in signature.
26+
If errors are reported by the test, you will fix the signature.
27+
You will be sure that you ship a correct signature finally.
28+
29+
The instrumentations are implemneted using `Module#prepend`.
30+
It defines a module with same name of methods, which asserts the type of arguments/return values and calls `super`.
31+
32+
## Type errors
33+
34+
If the test detects type errors, it will print error messages.
35+
36+
### ArgumentTypeError, BlockArgumentTypeError
37+
38+
The message means there is an unexpected type of argument or block argument.
39+
40+
```
41+
ERROR -- : [Kaigi::Speaker.new] ArgumentTypeError: expected `::String` (email) but given `:"matsumoto@soutaro.com"`
42+
```
43+
44+
### ArgumentError, BlockArgumentError
45+
46+
The message means there is an unexpected argument or missing argument.
47+
48+
```
49+
[Kaigi::Speaker.new] ArgumentError: expected method type (size: ::Symbol, email: ::String, name: ::String) -> ::Kaigi::Speaker
50+
```
51+
52+
### ReturnTypeError, BlockReturnTypeError
53+
54+
The message means the return value from method or block is incorrect.
55+
56+
```
57+
ERROR -- : [Kaigi::Conference#each_speaker] ReturnTypeError: expected `self` but returns `[#<Kaigi::Speaker:0x00007fb2b249e5a0 @name="Soutaro Matsumoto", @email=:"matsumoto@soutaro.com">]`
58+
```
59+
60+
### UnexpectedBlockError, MissingBlockError
61+
62+
The errors are reported when required block is not given or unused block is given.
63+
64+
```
65+
ERROR -- : [Kaigi::Conference#speakers] UnexpectedBlockError: unexpected block is given for `() -> ::Array[::Kaigi::Speaker]`
66+
```
67+
68+
### UnresolvedOverloadingError
69+
70+
The error means there is a type error on overloaded methods.
71+
The `ruby-signature` test framework tries to the best error message for overloaded methods too, but it reports the `UnresolvedOverloadingError` when it fails.
72+
73+
## Setting up the test
74+
75+
The design of the signature testing aims to be non-intrusive. The setup is done in two steps:
76+
77+
1. Loading the testing library
78+
2. Setting up the test through environment variables
79+
80+
### Loading the library
81+
82+
You need to require `ruby/signature/test/setup` for signature testing.
83+
You can do it using `-r` option through command line argument or the `RUBYOPT` environment variable.
84+
85+
```
86+
$ ruby -r ruby/signature/test/setup run_tests.rb
87+
$ RUBYOPT='-rruby/signature/test/setup' rake test
88+
```
89+
90+
When you are using Bundler, you may need to require `bundler/setup` explicitly.
91+
92+
```
93+
$ RUBYOPT='-rbundler/setup -rruby/signature/test/setup' bundle exec rake test
94+
```
95+
96+
### Environment variables
97+
98+
You need to specify `RBS_TEST_TARGET` to run the test, and you can customize the test with the following environment variables.
99+
100+
- `RBS_TEST_SKIP` (optional)
101+
- `RBS_TEST_OPT` (optional)
102+
- `RBS_TEST_LOGLEVEL` (optional)
103+
104+
`RBS_TEST_TARGET` is to specify the classes you want to test. `RBS_TEST_TARGET` can contain comma-separated class name pattern, which is one of an exact class name or with wildcard `*`.
105+
106+
- `RBS_TEST_TARGET=Foo::Bar,Foo::Baz` comma separated exact class names
107+
- `RBS_TEST_TARGET=Foo::*` using wildcard
108+
109+
`RBS_TEST_SKIP` is to skip some of the classes which matches with `RBS_TEST_TARGET`.
110+
111+
`RBS_TEST_OPT` is to pass the options for ruby signature handling.
112+
You may need to specify `-r` or `-I` to load signatures.
113+
The default is `-I sig`.
114+
115+
```
116+
RBS_TEST_OPT='-r set -r pathname -I sig'
117+
```
118+
119+
`RBS_TEST_LOGLEVEL` can be used to configure log level. Defaults to `info`.
120+
121+
So, a typical command line to start the test would look like the following:
122+
123+
```
124+
$ RBS_TEST_LOGLEVEL=error \
125+
RBS_TEST_TARGET='Kaigi::*' \
126+
RBS_TEST_SKIP='Kaigi::MonkeyPatch' \
127+
RBS_TEST_OPT='-rset -rpathname -Isig -Iprivate' \
128+
RUBY_OPT='-rbundler/setup -rruby/signature/test/setup' \
129+
bundle exec rake test
130+
```
131+
132+
## Testing tips
133+
134+
### Skipping a method
135+
136+
You can skip installing the instrumentation per-method basis using `rbs:test:skip` annotation.
137+
138+
```
139+
class String
140+
%a{rbs:test:skip} def =~: (Regexp) -> Integer?
141+
end
142+
```

doc/stdlib.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Stdlib Signatures Guide
2+
3+
This is a guide for contributing to `ruby-signature` by writing/revising stdlib signatures.
4+
5+
## Signatures
6+
7+
Signatures for standard libraries are located in `stdlib` directory. `stdlib/builtin` is for builtin libraries. Other libraries have directories like `stdlibt/set` or `stdlib/pathname`.
8+
9+
To write signatures see [syntax guide](`syntax.md`).
10+
11+
## Testing
12+
13+
We support writing tests for stdlib signatures.
14+
15+
### Writing tests
16+
17+
Put the test scripts in `test/stdlib` directory with `[class_name]_test.rb`, like `String_test.rb` and `File_test.rb`.
18+
The test scripts would look like the following:
19+
20+
```rb
21+
class StringTest < StdlibTest
22+
target String
23+
using hook.refinement
24+
25+
def test_gsub
26+
s = "string"
27+
s.gsub(/./, "")
28+
s.gsub("a", "b")
29+
s.gsub(/./) {|x| "" }
30+
s.gsub(/./, {"foo" => "bar"})
31+
s.gsub(/./)
32+
s.gsub("")
33+
end
34+
end
35+
```
36+
37+
You need two method calls, `target` and `using`.
38+
`target` method call tells which class is the subject of the class.
39+
`using hook.refinement` installs a special instrumentation for stdlib, based on refinements.
40+
And you write the sample programs which calls all of the patterns of overloads.
41+
42+
Note that the instrumentation is based on refinmenets and you need to write all method calls in the unit class definitions.
43+
If the execution of the program escape from the class definition, the instrumentation is disabled and no check will be done.
44+
45+
### Running tests
46+
47+
You can run the test with:
48+
49+
```
50+
$ bundle exec ruby bin/test_runner.rb # Run all tests
51+
$ bundle exec ruby bin/test_runner.rb String Hash # Run specific tests
52+
```

0 commit comments

Comments
 (0)