Skip to content

Commit 0626d54

Browse files
committed
Initial commit
0 parents  commit 0626d54

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+721
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.acre/
2+
aplcore

APLSource/GitHubAPIv3.aplc

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
:Class GitHubAPIv3
2+
⍝ This clas offers methods that allows one to communicate with GitHub by using GitHub's REST API from Dyalog APL.
3+
⍝ Note that this is version 3 of the GitHub API; the coming version 4 is **_not_** a REST interface!\\
4+
⍝ Most methods of this class become available only by instanciating the class. At the moment this might seem
5+
⍝ unnecessary because there is only one constructor requiring an owner's name, but this will change once OAuth
6+
⍝ is going to be implemented.\\
7+
⍝ The class came into being because APL Team needed such a class in order to deal with the members of the APLTree
8+
⍝ and the APL-cation projects.\\
9+
⍝ Note that this class needs **at least Dyalog 16.0** (⎕JSON).\\
10+
⍝ The project lives on <https://github.com/aplteam/GitHubAPIv3>\\
11+
⍝ It is part of the [APLTree library](https://github.com/aplteam/apltree/wiki)
12+
⍝ Kai Jaeger ⋄ APL Team Ltd
13+
14+
:Include ##.APLTreeUtils
15+
16+
⎕IO←1 ⋄ ⎕ML←1
17+
18+
∇ r←Version
19+
:Access Public Shared
20+
r←'GitHub' '0.0.1.3' '2018-12-31'
21+
22+
23+
∇ History
24+
:Access Public Shared
25+
⍝ * 0.0.1
26+
⍝ First release.
27+
28+
29+
:Property owner
30+
∇ r←Get
31+
r←_owner
32+
33+
:EndProperty
34+
35+
∇ make1(owner)
36+
:Access Public Instance
37+
:Implements Constructor
38+
_owner←owner
39+
40+
41+
∇ (rc msg ns)←GetLatestRelease repoName;gitPath
42+
:Access Public Instance
43+
⍝ Returns data regarding the latest release. Drafts and beta releases are ignored.\\
44+
⍝ You must use https:// (protocol), or do not specify a protocol at all.\\
45+
⍝ `⍵` : Name of the repository.\\
46+
⍝ `←` : Same as `GetJson` - see there for details.
47+
gitPath←'https://api.github.com/repos/',_owner,'/',repoName,'/releases/latest'
48+
(rc msg ns)←GetJson gitPath
49+
:If 0=rc
50+
ns.⎕DF '[JSON object: Release (',repoName,'-',ns.tag_name,')]'
51+
:EndIf
52+
53+
54+
∇ (rc msg ns)←GetReleaseByTagName(repoName tagName);gitPath
55+
:Access Public Instance
56+
⍝ Fetches the release with `tagName` from `repoName`.\\
57+
⍝ You must use https:// (protocol), or do not specify a protocol at all.
58+
⍝ ## Right argument
59+
⍝ * Name of the repository
60+
⍝ * The tag name
61+
⍝ `←` : Same as `GetJson` - see there for details.
62+
gitPath←'https://api.github.com/repos/',_owner,'/',repoName,'/releases/tags/',tagName
63+
(rc msg ns)←GetJson gitPath
64+
:If 0=rc
65+
ns.⎕DF'[JSON object: release-',tagName,']'
66+
:EndIf
67+
68+
69+
∇ (rc msg ns)←GetAllReleases repoName;gitPath
70+
:Access Public Instance
71+
⍝ Returns all releases of a given repository.\\
72+
⍝ Notes:
73+
⍝ * Pre-release are included
74+
⍝ * Users without "Push" access will **_not_** see any draft releases\\
75+
⍝ You must use https:// (protocol), or do not specify a protocol at all.
76+
⍝ `⍵` : Name of the repository.\\
77+
⍝ `←` : Same as `GetJson` - see there for details.
78+
gitPath←'https://api.github.com/repos/',_owner,'/',repoName,'/tags'
79+
(rc msg ns)←GetJson gitPath
80+
81+
82+
∇ (rc msg ns)←GetAllRepos;gitPath
83+
:Access Public Instance
84+
⍝ Returns data of all **public** repositories for the current owner.\\
85+
gitPath←'https://api.github.com/users/',(_owner),'/repos'
86+
(rc msg ns)←GetJson gitPath
87+
88+
89+
∇ number←CastTagname2Number text;vec;bool
90+
:Access Public Shared
91+
⍝ Takes something like `v12.34.567` or `v123.4.5.6789` and returns 1234567 and 1234567.1234 respectively.\\
92+
⍝ The items are called major.minor.patch.built with "built" being optional.\\
93+
⍝ In case that is impossible (because `text` does not fulfil the criteria) `⍬` is returned.\\
94+
⍝ Assumptions:
95+
⍝ * `text` may or may not start with a non-digit. If there is one it will be removed.\\
96+
⍝ Therefore both `1.2.3` and `v.1.2.3` are valid input.
97+
⍝ * The remaining `text` must consist of nothing but digits and dots.
98+
⍝ * The first two numbers ("major" and "minor") must not be bigger than 99.
99+
⍝ * The third number ("path") must not be bigger than 999.
100+
⍝ * The optional last (forth) number must not be bigger than 99999.
101+
⍝ * `text` must come either with three numbers (as in `1.2.3`) or with four number (as in `1.2.3.9999`).
102+
⍝ However, even if all assumptions are fulfilled but the result is zero there is still a `⍬` returned.
103+
⍝ Note that leading zeros are mermitted. Therefor 1.2.3.001 is **_not_** a valid input.
104+
⍝ Examples:
105+
⍝ + 1.20.333 transforms into 120333
106+
⍝ + 12.12.123.12345 transforms into 1212123.12345
107+
⍝ If the tag name does not fulfil the assumptions the conversion might crash. In that case `⍬` is
108+
⍝ returned as result.
109+
number←⍬
110+
(bool vec)←'.'⎕VFI{⍵↓⍨~⎕D∊⍨⊃⍵}text
111+
:If 3 4∊⍨⍴bool
112+
vec←{⍵↑⍨3⌈4⌊⍴⍵}↑vec
113+
:If 3=⍴vec
114+
:If ∧/100 100 1000>vec
115+
:AndIf 0=number←100 100 1000⊥vec
116+
number←⍬
117+
:EndIf
118+
:Else
119+
:If ~∧/100 100 1000 100000>vec
120+
number←⍬
121+
:ElseIf 0=number←100 100 1000 100000⊥vec
122+
number←⍬
123+
:EndIf
124+
number÷←100000
125+
:EndIf
126+
:EndIf
127+
128+
129+
∇ (rc msg endpoints)←GetAllEndpoints;gitPath;msg;rc
130+
:Access Public Shared
131+
⍝ This method returns all REST endpoints offered by the API.\\
132+
⍝ It returns a namespace. Use `endpoint`'s built-in `List` function for an overview.
133+
⍝ `endpoints` is empty in case of an error.
134+
gitPath←'https://api.github.com/'
135+
endpoints←⎕NS''
136+
(rc msg endpoints)←GetJson gitPath
137+
:If 0=rc
138+
endpoints.⎕FX'r←List' 'r←{⍵,[1.5]⍎¨⍵}⎕NL-2'
139+
:EndIf
140+
141+
142+
⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝ Private stuff
143+
144+
GetNoOfPages←{
145+
⍝ Takes HTTP headers and returns the number if pages. If there are not any a zero is returned.
146+
headers←⍵
147+
bool←'Link:'{⍺∘≡¨(⍴⍺)↑¨⍵}headers
148+
0=+/bool:0
149+
link←(bool⍳1)⊃headers
150+
buff←' 'Split link
151+
last←(¯1+⍴buff)⊃buff
152+
buff←{⍵{(⍴'?page-')↓⍵[2]↑⍵[1]↓⍺}⊃'(&|\?)page=[0-9]{1,}'⎕S 0 1⊣⍵}last
153+
⊃⊃(//)⎕VFI buff
154+
}
155+
156+
GetLinkToNextPage←{
157+
⍝ Takes HTTP headers and returns the link pointing to the next page, if any
158+
headers←⍵
159+
bool←'Link:'{⍺∘≡¨(⍴⍺)↑¨⍵}headers
160+
0=+/bool:0
161+
link←(bool⍳1)⊃headers
162+
buff←' 'Split link
163+
1↓¯2↓⊃1↓buff
164+
}
165+
166+
SplitHeaders←{
167+
headers←⍵↑⍨1⍳⍨(⎕UCS 10 13)⍷⍵
168+
(⎕UCS 13 10)Split headers
169+
}
170+
171+
∇ txt←GetText obj;ts
172+
ts←⎕NEW StreamReader obj ⍝ text stream
173+
txt←ts.ReadToEnd
174+
175+
176+
∇ (rc msg ns)←GetJson gitPath;cp;ServicePointManager;req;res;data;WebRequest;i;noOfPages;headers;owner
177+
⍝ Takes a path which must specify a valid GitHub API URL and returns the data from GitHub.\\
178+
⍝ `⍵`: Project URL, for example 'api.github.com/repos/aplteam/testrepo/releases/latest'
179+
⍝ `rc`
180+
⍝ : Either 0 for okay or an error code.\\
181+
⍝ `msg`
182+
⍝ : Is empty in case `rc←→0` but might offer addtional information otherwise.
183+
⍝ `ns`
184+
⍝ : Namespace with the data received from GitHub.
185+
:If 0=⎕NC'_owner'
186+
owner←'APL GitHub API'
187+
:Else
188+
owner←_owner
189+
:EndIf
190+
gitPath←'https://'{⍵,⍨⍺/⍨⍺≢(⍴⍺)↑⍵}Lowercase gitPath
191+
rc←0
192+
msg←''
193+
ns←⎕NS''
194+
:Trap 90
195+
⎕USING←'System.Net,system.dll' 'System.IO' 'System.Text'
196+
cp←ServicePointManager.SecurityProtocol ⍝ current protocol
197+
ServicePointManager.SecurityProtocol←SecurityProtocolType.Tls12
198+
req←WebRequest.CreateHttp⊂gitPath
199+
req.UserAgent←owner ⍝ MUST NOT be empty: required by the github api!
200+
req.Accept←'Accept: application/vnd.github.v3+json'
201+
res←req.GetResponse
202+
ServicePointManager.SecurityProtocol←cp
203+
:If res.StatusCode≠res.StatusCode.OK
204+
rc←res.StatusCode
205+
msg←'HTTP error'
206+
:Return
207+
:EndIf
208+
ns←⎕JSON GetText res.GetResponseStream
209+
headers←SplitHeaders⍕res.Headers
210+
:If 0<noOfPages←GetNoOfPages headers
211+
:For i :In 1↓⍳noOfPages
212+
req←WebRequest.CreateHttp⊂GetLinkToNextPage headers
213+
req.UserAgent←owner ⍝ MUST NOT be empty: required by the github api!
214+
res←req.GetResponse
215+
:If res.StatusCode≠res.StatusCode.OK
216+
rc←res.StatusCode
217+
msg←'HTTP error'
218+
:Return
219+
:EndIf
220+
ns,←⎕JSON GetText res.GetResponseStream
221+
:EndFor
222+
:EndIf
223+
:Else
224+
rc←90
225+
msg←{⍵↓⍨1+⍵⍳':'}{⍵↑⍨1⍳⍨(⎕UCS 8 10)⍷⍵}⍕⎕EXCEPTION
226+
:EndTrap
227+
228+
229+
:EndClass

APLSource/TestCases/Cleanup.aplf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Cleanup
2+
⎕EX'∆MyGitHubAPI'

APLSource/TestCases/E.aplf

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{list}←E list
2+
⍝ Get all functions into the editor starting their names with "Test_".
3+
:If 0∊⍴list
4+
list←'T'⎕NL 3
5+
:ElseIf 2=⍴⍴list
6+
:If 2=≡list
7+
list←{⎕ML←1 ⋄ ↑⍵}list[;⎕IO]
8+
:Else
9+
list←{⎕ML←1 ⋄ ↑⍵}{⍵↑⍨+/∧\⍵≠' '}¨{⍵↓⍨+/∧\' '=⍵}¨↓list
10+
:EndIf
11+
:Else
12+
'Invalid right argument'⎕SIGNAL 11
13+
:EndIf
14+
{(0∊⍴⍵): ⋄ ⎕ML←1 ⋄ ⎕ED↑⍵}&↓'Test_'{⍵⌿⍨⍺∧.=⍨(⍴,⍺)↑[1+⎕IO]⍵}list

APLSource/TestCases/FailsIf.aplf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FailsIf←{
2+
⍝ Usage : →FailsIf x, where x is a boolean scalar
3+
⎕TRAP←(999 'E' '(⎕IO⊃⎕DM)⎕SIGNAL 999')(0 'N')
4+
PassesIf~⍵ ⍝ Just PassesIf on negation
5+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
r←{startIn}FindSpecialString what
2+
⍝ Use this to search for stuff like "CHECK" or "TODO" enclosed between `⍝` (⍵).
3+
⍝ Without left argument the search starts in #.
4+
⍝ However, at the time of writing the user command ]locate does not work on #.
5+
⍝ Reported as bug <01355> to Dyalog on 2017-04-24.
6+
startIn←{0<⎕NC ⍵:⍎⍵ ⋄ '#'}'startIn'
7+
r←⍉1↓[1+⎕IO]⎕SE.UCMD'locate "',what,'" -return=count -objects=',⍕startIn
8+
:If 0<1↑⍴r←(0<r[;⎕IO+1])⌿r ⍝ Drop those with no hits
9+
r[;⎕IO]←{2>'#'+.=⍵:⍵ ⋄ {⎕IO←0 ⋄ ⌽⍵↑⍨1+⍵⍳'#'}⌽⍵}¨r[;⎕IO] ⍝ Circumvent bug <01356>
10+
:EndIf

APLSource/TestCases/G.aplf

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
r←G;A
2+
⍝ Prints all groups to the session.
3+
:If 9=#.⎕NC'APLTreeUtils'
4+
A←#.APLTreeUtils
5+
:ElseIf 9=⎕NC'APLTreeUtils'
6+
A←APLTreeUtils
7+
:ElseIf 9=##.⎕NC'APLTreeUtils'
8+
A←##.APLTreeUtils
9+
:ElseIf 9∊{⎕ML←1 ⋄ ⊃¨⍵}⎕RSI.⎕NC⊂'APLTreeUtils'
10+
A←({⎕ML←1 ⋄ ⊃⍵}(9={⎕ML←1 ⋄ ⊃¨⍵}⎕RSI.⎕NC⊂'APLTreeUtils')/⎕RSI).APLTreeUtils
11+
:ElseIf 9∊⊃¨⎕RSI.##.⎕NC⊂'APLTreeUtils'
12+
A←({⎕ML←1 ⋄ ⊃⍵}(9={⎕ML←1 ⋄ ⊃¨⍵}⎕RSI.##.⎕NC⊂'APLTreeUtils')/⎕RSI.##).APLTreeUtils
13+
:Else
14+
'Missing: APLTreeUtils'⎕SIGNAL 6
15+
:EndIf
16+
r←↓'Test_'{⍵⌿⍨((⍴⍺)↑[1+⎕IO]⍵)∧.=⍺}'T'⎕NL 3
17+
:If ~0∊⍴r←(2≤'_'+.=⍉{⎕ML←1 ⋄ ↑⍵}r)⌿r
18+
:AndIf ~0∊⍴r←{⎕ML←1 ⋄ ↑⍵}∪{⎕IO←0 ⋄ ⍵↓⍨-1+(⌽⍵)⍳'_'}¨' '~⍨¨r
19+
r←r[⍋A.Lowercase r;]
20+
:EndIf
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
r←{label}GoToTidyUp flag
2+
⍝ Returns either an empty vector or "Label" which defaults to ∆TidyUp
3+
⍝ but signals 999 when flag=1 and "stopFlag" exists and is 1.
4+
:If 1=flag
5+
:AndIf 0<⎕NC'stopFlag'
6+
:AndIf stopFlag
7+
⎕SIGNAL 999
8+
:EndIf
9+
label←{(0<⎕NC ⍵):⍎⍵ ⋄ r←⍎'∆TidyUp'}'label'
10+
r←flag/label

APLSource/TestCases/Initial.aplf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Initial
2+
∆MyGitHubAPI←⎕NEW ##.GitHubAPIv3(,⊂'aplteam')

APLSource/TestCases/L.aplf

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
r←{numbers}L group;A;r2;⎕IO;⎕ML;b
2+
⍝ Prints a list with all test cases and the first comment line to the session.
3+
⍝ If "group" is not empty then it will print only that group (case independent).
4+
⍝ May or may not start with "Test_"
5+
⍝ If "numbers" is defined only test cases carrying those numbers are printed.
6+
⎕IO←1 ⋄ ⎕ML←1
7+
:If 9=#.⎕NC'APLTreeUtils'
8+
A←#.APLTreeUtils
9+
:ElseIf 9=⎕NC'APLTreeUtils'
10+
A←APLTreeUtils
11+
:ElseIf 9=##.⎕NC'APLTreeUtils'
12+
A←##.APLTreeUtils
13+
:ElseIf 9∊⊃¨⎕RSI.⎕NC⊂'APLTreeUtils'
14+
A←(⊃(9=⊃¨⎕RSI.⎕NC⊂'APLTreeUtils')/⎕RSI).APLTreeUtils
15+
:ElseIf 9∊⊃¨⎕RSI.##.⎕NC⊂'APLTreeUtils'
16+
A←(⊃(9=⊃¨⎕RSI.##.⎕NC⊂'APLTreeUtils')/⎕RSI.##).APLTreeUtils
17+
:Else
18+
'Missing: APLTreeUtils'⎕SIGNAL 6
19+
:EndIf
20+
numbers←{(0<⎕NC ⍵):⍎⍵ ⋄ ⍬}'numbers'
21+
r2←↓'Test_'{⍵⌿⍨((⍴⍺)↑[2]⍵)∧.=⍺}'T'⎕NL 3
22+
:If ~0∊⍴group
23+
group←A.Lowercase'test_'{((⍺≢(⍴⍺)↑⍵)/⍺),⍵}group
24+
r2←(({⎕ML←1 ⋄ ↑⍵}A.Lowercase(⍴group)↑¨r2)∧.=group)⌿r2
25+
:EndIf
26+
:If 0∊⍴r2
27+
r←0 1⍴''
28+
:Else
29+
:If ~0∊⍴numbers
30+
r2←(({⍎⍵↑⍨-(-1)+'_'⍳⍨⌽⍵}¨r2)∊numbers)⌿r2
31+
:EndIf
32+
r2←r2,[1.5]{{⍵↓⍨+/∧\' '=⍵}{⍵↓⍨⍵⍳'⍝'}{⎕ML←3 ⋄ ∊⍵}1↑1↓⎕NR ⍵}¨r2
33+
r2←r2[⍋↑A.Lowercase r2[;1];]
34+
r←⎕FMT r2
35+
:If ⎕PW<2⊃⍴r
36+
b←⎕PW<⊃∘⍴¨A.dtb↓r
37+
(b⌿r)←↑(2⊃⍴r)↑¨((⎕PW-3)↑¨↓b⌿r),¨⊂'...'
38+
r←⎕PW↑[2]r
39+
:EndIf
40+
:EndIf

0 commit comments

Comments
 (0)