Browse Source

New repo for this module

master
Sebastian 1 year ago
commit
46fa015975
Signed by: imo GPG Key ID: AFA10CBFE4391C0C
5 changed files with 509 additions and 0 deletions
  1. +87
    -0
      .drone.yml
  2. +24
    -0
      LICENSE
  3. +153
    -0
      README.md
  4. +153
    -0
      cc.lua
  5. +92
    -0
      spec/cc_spec.lua

+ 87
- 0
.drone.yml View File

@ -0,0 +1,87 @@
kind: pipeline
name: lint
steps:
- name: lint
image: imolein/luarocks:5.3
commands:
- luarocks install luacheck
- luacheck lib/cc.lua
steps:
- name: pre-test
image: alpine:3.10
commands:
- apk add wget
- mkdir ./libs
- wget -P ./lib/ https://raw.githubusercontent.com/rxi/json.lua/master/json.lua
- wget -P ./lib/ https://codeberg.org/imo/gambiarra/raw/branch/master/src/gambiarra.lua
---
kind: pipeline
name: lua5.1
steps:
- name: test
image: imolein/luarocks:5.1
commands:
- echo $LUA_PATH
- echo $LUA_CPATH
trigger:
status:
- success
depends_on:
- lint
---
kind: pipeline
name: lua5.2
steps:
- name: test
image: imolein/luarocks:5.2
commands:
- exit 0
trigger:
status:
- success
depends_on:
- lint
---
kind: pipeline
name: lua5.3
steps:
- name: test
image: imolein/luarocks:5.3
commands:
- exit 0
trigger:
status:
- success
depends_on:
- lint
---
kind: pipeline
name: lua5.4
steps:
- name: test
image: imolein/luarocks:5.4
commands:
- exit 0
trigger:
status:
- success
depends_on:
- lint

+ 24
- 0
LICENSE View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>

+ 153
- 0
README.md View File

@ -0,0 +1,153 @@
# Coding Challenge Modul
Eine kleines Modul, zum Challenge fetchen und submitten. Außerdem kann man sich die durchschnittliche Dauer für alle Lösungsdurchläufe ausgeben lassen.
Es wurde von mir geschrieben um wiederkehrende Aufgaben wie fetchen, submitten etc. beim Lösen der Coding Challenges vom Youtube Channel [The Morpheus Tutorials](https://www.youtube.com/user/TheMorpheus407) zu vereinfachen. Meine Lösungen die dieses Modul nutzen liegen hier: [coding_challenge_loesung](https://git.kokolor.es/imo/coding_challenge_loesung)
## Nutzungsbeispiel
```lua
> CC = require('cc')
> cc = CC.new(1, { nojson = true })
> cc:solve(function(ch) return { token = tostring(ch) } end, true)
Success 200
> cc:runtime_avg()
0.000003s
```
## Inhalt
* [Abhängigkeiten](#abhängigkeiten)
* [Funktionen](#funktionen)
* [new()](#new)
* [fetch()](#fetch)
* [solve()](#solve)
* [submit()](#submit)
* [runtime_avg()](#submit)
* [Beispiele](#beispiele)
* [Tests](#tests)
## Abhängigkeiten
* lua >= 5.1
* [lua-curl](https://github.com/Lua-cURL/Lua-cURLv3)
* [json.lua](https://github.com/rxi/json.lua)
* [chronos](https://github.com/ldrumm/chronos)
## Funktionen
### `new(cn[, opts])`
Erstellt neues Coding Challenge Instanz.
**Parameter:**
* _cn:_ (**number**) Nummer der Challenge, also z.B. 3 für die dritte Challenge
* _opts:_ (**table**) options (**opt**)
* _opts.nojson:_ (**bool**) auf `true` setzen wenn die Challenge nicht als JSON bereitgestellt wird
* _opts.get_path:_ (**string**) der Pfad wird der get URL hinzugefügt. Bei einer Challenge gab es mal die Möglichkeit die Werte sortiert übermittelt zu bekommen indem mal `/sorted` als Pfad angab.
* _opts.post_path:_ (**string**) selbe wie `get_path` nur `POST` request, also `submit()`
**Returns:**
(**table**) Objekt table
**Usage:**
```lua
local cc = CC.new(15)
```
### `fetch()`
Abrufen einer Challenge.
**Parameter:**
`nil`
**Returns:**
(**table|string**) wenn `opts.nojson` auf `true` gesetzt ist, wird ein String returned, sonst eine Table.
**Usage:**
```lua
local challenge = cc:fetch()
```
### `solve(fn, sbmt)`
Lösen der Challenge. Dies ist eigentlich nur ein Wrapper für die Lösungsfunktion, welcher die Ausführungszeit misst. Wird dies nicht benötigt kann man auch die Lösungsfunktion ohne `solve()` nutzen.
`solve()` ruft `fetch()` auf um eine neue Challenge zu bekommen. Sollte man also eine `for`-Loop nutzen, brauch man nur `solve()` nutzen.
**Parameter:**
* _fn:_ (**function**) die Funktion, welche die Challenge löst. `fn` nimmt ein Parameter (_table_) entgegen.
* _sbmt:_ (**bool**) Wenn `true` wird direkt `submit()` mit dem zurückgegebenen Wert von `fn` aufgerufen.
**Returns:**
(**void** | **string**, **number**) Das Ergebnis | Wenn `sbmt = true`
**Usage:**
```lua
local result = cc:solve(function(c) return c.word end)
```
### `submit(data[, header])`
Übermitteln der Lösung.
**Parameter:**
* _data:_ (**table**) Übergabe der Daten in Form einer table, welche dann in JSON umgewandelt wird.
* _headers:_ (**table**) Werden zusätzliche Header benötigt, können diese als Tabelle übergeben werden
**Returns:**
(**string**, **number**) Return value 1 ist der Status Text ob _Success_ oder _Error_ und value 2 ist der HTTP Statuscode
**Usage:**
```lua
local resp = cc:post({ token = 'blubb' })
```
```lua
local c2 = c2:post({
token = 'blubb'
},
{
headers = {
['X-Clacks-Overhead'] = 'GNU Terry Pratchett'
}
})
```
### `runtime_avg()`
Durchschnittliche Zeit aller Lösungs Durchläufe. Für Benchmark.
**Parameter:**
`nil`
**Returns:**
(**string**) the time in seconds
**Usage:**
```lua
cc:runtime_avg() -- Output: 0.000004
```
## Beispiele
* C15: [palindrome.lua](https://git.kokolor.es/imo/coding_challenge_loesung/src/branch/master/c15/palindrome.lua)
## Tests
Um die Tests laufen zu lassen wird [gambiarra](https://codeberg.org/imo/gambiarra) benötigt. Einfach **gambiarra.lua** herunterladen und in den Ordner `spec/` packen und die Tests mit `lua lib/spec/cc_spec.lua` ausführen.

+ 153
- 0
cc.lua View File

@ -0,0 +1,153 @@
-- Lua module for coding challenges from Morpheus Tutorials
local time = require('chronos')
local curl = require('lcurl')
local json = require('json')
local CC = {
_VERSION = '1.2.0',
_DESCRIPTION = 'Lua module for fetching and submitting challenges from The Morpheus coding challenge',
_URL = 'https://git.kokolor.es/imo/coding_challenge_loesung',
_LICENCE = 'The Unlicense'
}
local USERAGENT = ('cc.lua/%s (%s)'):format(CC._VERSION, CC._URL)
local URL = 'https://cc.the-morpheus.de/%s/%d/'
-- private functions
--- build url string
-- url (string)
-- args (string)
local function _build_url(url, args)
if type(args) == 'string' then
return url .. args .. '/'
else
return url
end
end
--- build headers for post request
-- headers (table)
local function _build_headers(headers)
local new_headers = { 'Content-Type: application/json' }
if not headers then return new_headers end
for k, v in pairs(headers) do
table.insert(new_headers, string.format('%s: %s', k, v))
end
return new_headers
end
--- GET request function
-- url (string)
-- nojson (bool)
local function _get(url, nojson)
local queue = {}
curl.easy()
:setopt_url(url)
:setopt(curl.OPT_USERAGENT, USERAGENT)
:setopt_writefunction(function(buf)
table.insert(queue, buf)
end)
:perform()
:close()
if not nojson then
return json.decode(table.concat(queue))
else
return table.concat(queue)
end
end
--- POST request function
-- url (string)
-- data (table)
-- headers (table)
local function _post(url, data, headers)
local jdata = json.encode(data)
local queue = {}
local req = curl.easy()
:setopt_url(url)
:setopt(curl.OPT_USERAGENT, USERAGENT)
:setopt_httpheader(_build_headers(headers))
:setopt_postfields(jdata, #jdata)
:setopt_writefunction(function(buf)
table.insert(queue, buf)
end)
:perform()
local status = req:getinfo(curl.INFO_RESPONSE_CODE)
req:close()
return table.concat(queue), status
end
-- private methods
--- fetches the challenge
function CC:fetch()
return _get(self.challenges_url, self.nojson)
end
--- coding challenge solve method
-- fn (function) = callback function which solves the challenge
-- submt (bool) = submit the challenge after solving it
-- Returns JSON data as table or plain data if nojson is true
function CC:solve(fn, sbmt)
local challenge = self:fetch()
local start = time.nanotime()
local result = fn(challenge)
table.insert(self.duration, time.nanotime() - start)
if sbmt then return self:submit(result) end
return result
end
--- coding challenge post method
-- data (table) = the solutions data
-- headers (table) = table of custom headers if needed
-- Returns text and status code
function CC:submit(data, headers)
return _post(self.solutions_url, data, headers)
end
--- returns the average time of all runs
function CC:runtime_avg()
local t = 0
for _, v in ipairs(self.duration) do
t = t + v
end
return string.format('%f', t / #self.duration)
end
-- public methods
local cc = {}
--- create new coding challenge instance
-- cn (number) = challenge number
-- opts (table) = options
-- opts.nojson (bool) = expects the challenge as plain text if true
-- opts.get_path (string) = path is added to the get url
-- opts.post_path (string) = path is added to the post url
function cc.new(cn, opts)
opts = opts or {}
local obj = {
duration = {},
solutions_url = _build_url(URL:format('solutions', cn), opts.post_path),
challenges_url = _build_url(URL:format('challenges', cn), opts.get_path),
nojson = opts.nojson or false
}
return setmetatable(obj, { __index = CC })
end
return cc

+ 92
- 0
spec/cc_spec.lua View File

@ -0,0 +1,92 @@
package.path = package.path .. ';./lib/?.lua;./lib/spec/?.lua;'
local test = require('gambiarra')
local CC = require('cc')
local passed, failed, errored = 0, 0, 0
test(function(ev, fn, msg)
if ev == 'begin' then
io.write(string.format('-- Tests started for: %s\n', fn))
elseif ev == 'pass' then
passed = passed + 1
io.write(string.format('[✓] %s\n', msg))
elseif ev == 'fail' then
failed = failed + 1
io.write(string.format('[✗] %s\n', msg))
elseif ev == 'except' then
errored = errored + 1
io.write(string.format('[!] %s\n', msg))
elseif ev == 'end' then
io.write('\n')
end
end)
local function report()
local count = passed + failed + errored
local stats = string.format('Passed: %d/%d; Failed: %d/%d; Errored: %d/%d', passed, count, failed,
count, errored, count)
if passed == count then
io.write(string.format('--[ %s ]--\n\n', stats))
os.exit(0)
else
io.write(string.format('--[ %s ]--\n\n', stats))
os.exit(1)
end
end
local function cc1(ch)
return { token = tostring(ch) }
end
io.write('--- Started tests for coding challenge module\n\n')
test('new()', function()
local cc = CC.new(1)
ok(eq(cc.challenges_url, 'https://cc.the-morpheus.de/challenges/1/'), 'Challenge url was created correctly')
ok(eq(cc.solutions_url, 'https://cc.the-morpheus.de/solutions/1/'), 'Solutions url was created correctly')
ok(eq(cc.nojson, false), 'nojson option is false by default')
cc = nil
cc = CC.new(1, { nojson = true, get_path = 'sorted' })
ok(eq('https://cc.the-morpheus.de/challenges/1/sorted/', cc.challenges_url), 'Given get_path is attached to challenges URL')
ok(eq(cc.nojson, true), 'nojson option was set to true')
end)
test('fetch()', function()
local cc = CC.new(1, { nojson = true })
local res = cc:fetch()
ok(eq(type(res), 'string'), 'Challenge was fetched correctly')
end)
test('solve()', function()
local cc = CC.new(1, { nojson = true })
local res, s = cc:solve(cc1)
ok(eq(type(res), 'table'), 'Solutions is a table')
ok(eq(res.token ~= nil, true), 'Solutions has key "token"')
res, s = cc:solve(cc1, true)
ok(eq(res:find('Success') ~= nil, true), 'After solution was solved it was submitted successfully')
ok(eq(s, 200), 'Status code is 200')
end)
test('submit()', function()
local cc = CC.new(1, { nojson = true })
local res, s = cc:submit(cc:solve(cc1))
ok(eq(res:find('Success') ~= nil, true), 'Solution was submitted successfully')
ok(eq(s, 200), 'Status code is 200')
end)
test('runtime_avg()', function()
local cc = CC.new(1, { nojson = true })
cc:solve(cc1, true)
ok(eq(cc:runtime_avg():match('%d+%.%d+') ~= nil, true), 'Runtime was returned')
end)
report()

Loading…
Cancel
Save