How do I make a modulino in Perl6? - system

I want to make a modulino (a file that can run as either a module or a script) in Perl6.
The following code "processes" file names from the command line:
sub MAIN ( *#filenames )
{
for #filenames -> $filename
{
say "Processing $filename";
}
}
Saving this as main.pm6 I can run it and it works:
perl6 main.pm6 hello.txt world.txt
Processing 'hello.txt'
Processing 'world.txt'
So, I want this to be a module so that I can add functions and make testing it easier. However, when I add a module declaration to it, it no longer outputs anything:
module main;
sub MAIN ( *#filenames )
{
for #filenames -> $filename
{
say "Processing '$filename'";
}
}
Which results in nothing output:
perl6 main.pm6 hello.txt world.txt
So, how can I build a modulino in Perl6?
I'm using Perl6 running on MoarVM from the January 2015 release of Rakudo Star.
UPDATE:
When I try wrapping the module in braces:
module main
{
sub process (#filenames) is export
{
for #filenames -> $filename
{
say "Processing '$filename'";
}
}
};
sub MAIN ( *#filenames )
{
process(#filenames)
}
I also get errors:
===SORRY!=== Error while compiling main.pm6
Undeclared routine:
process used at line 14. Did you mean 'proceed'?

The MAIN sub needs to be declared outside the module, but it still must be able to see process.
There are multiple ways to achieve this, eg by not declaring a module at all
sub process(#filenames) {
for #filenames -> $filename {
say "Processing '$filename'";
}
}
sub MAIN(*#filenames) {
process(#filenames);
}
by making process our-scoped and calling it by its longname
module main {
our sub process(#filenames) {
for #filenames -> $filename {
say "Processing '$filename'";
}
}
}
sub MAIN(*#filenames) {
main::process(#filenames);
}
or by exporting process and importing it in the body of MAIN
module main {
sub process(#filenames) is export {
for #filenames -> $filename {
say "Processing '$filename'";
}
}
}
sub MAIN(*#filenames) {
import main;
process(#filenames);
}
In my opinion the most appropriate option is to add MAIN to the module and import it into the script's mainline. This way, everything declared within the module is visible within MAIN without having to explicitly export everything:
module main {
sub process(#filenames) {
for #filenames -> $filename {
say "Processing '$filename'";
}
}
sub MAIN(*#filenames) is export(:MAIN) {
process(#filenames);
}
}
import main :MAIN;
Note that this does not export MAIN by default, ie users of your module will only get it if they provide the :MAIN tag.

Related

How do I use Kotlin Function inside TeamCity KotlinScriptBuildStep

I have a TeamCity settings.kts code with configuration of build and I want to write a function inside settings.kts or in a file defined next to settings.kts and use the function inside kotlinScript step that is (KotlinScriptCustomBuildStep). How do I call the function (so that I don't need to write big logic inside kotlinScript.content?
//setting.kts
steps {
kotlinScript {
name = "Doing something"
//language=kotlin
content = """
import JiraReport
println(JiraReport().findLastMessage())
....
""".trimIndent()
}
//JiraReport class
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
class JiraReport {
fun findLastMessage(): String {
...
}

Unit can't be called in this context by implicit receiver

I am following this Kotlin example (https://www.jetbrains.com/help/teamcity/kotlin-dsl.html#Editing+Kotlin+DSL) and trying to write a kotlin script for my CI.
This is my code snippet
steps {
script {
name = "Style check"
id("StyleCheck")
enabled = false
scriptContent = """
#!/bin/bash
make docker run="make ci lint"
""".trimIndent()
}
I get an Error for the id() call which says
What does the error message mean?
How can I use id() call as given in the example?
This error happens because the method id(String) is defined in an outer scope, and to prevent you from accidentally using the wrong method, Kotlin gives you a compiler error.
In your case you should make sure that there's no other id that you want to use. Perhaps you wanted to use the property named id instead of the method?
Note that neither of the options below may have the same effect as you want. There might be a reason why the API is written like this to not allow you to call methods from outer receivers from inside inner scopes.
In order to use an explicit receiver, you can use this#methodName to call it. In this case, this#steps.
steps {
script {
name = "Style check"
this#steps.id("StyleCheck")
enabled = false
scriptContent = """
#!/bin/bash
make docker run="make ci lint"
""".trimIndent()
}
}
To understand exactly what's going on, and another way of using an explicit receiver, you could also do something like the below, where you save the this scope in a variable and then call it inside the script scope.
steps {
val stepsThis = this
script {
name = "Style check"
stepsThis.id("StyleCheck")
enabled = false
scriptContent = """
#!/bin/bash
make docker run="make ci lint"
""".trimIndent()
}
}
I also got the same error when working with jetpack compose when I was writing the code below.
In my case the context was not clear.
It was giving this error.
'fun item(key: Any? = ..., content: LazyItemScope.() -> Unit): Unit' can't be called in this context by implicit receiver. Use the explicit one if necessary
It says Unit can't be called in this context. Therefore I changed the context and everything got right.
Error code:
LazyColumn {
items(items = items) { word ->
if (word != null) {
WordColumnItem(word = word) {
onSelected(word)
}
}
//Notice the context
if(items.itemCount == 0) {
item(
content = { EmptyContent("No words") }
)
}
}
}
Correct code:
LazyColumn {
items(items = items) { word ->
if (word != null) {
WordColumnItem(word = word) {
onSelected(word)
}
}
}
//context changed.
if(items.itemCount == 0) {
item(
content = { EmptyContent("No words") }
)
}
}
Although I don't know how to use the explicit receiver.(if necessary)
Solution 1) I think error is clear.
2) You can use explicit receiver.

access environment variables in jenkins shared library code

When I use my new shared library I cannot access environment variables for any src class which is executed either directly by the Jenkinsfile or via a var/*.groovy script. This problem persists even when I add withEnv to the var/*groovy script.
What is the trick to get environment variables to propagate to jenkins shared library src class execution?
Jenkinsfile
withEnv(["FOO=BAR2"]) {
println "Jenkinsfile FOO=${FOO}"
library 'my-shared-jenkins-library'
lib.displayEnv()
Shared Library var/lib.groovy
def displayEnv() {
println "Shared lib var/lib FOO=${FOO}"
MyClass c = new MyClass()
}
Shared Library src/MyClass.groovy
class MyClass() {
MyClass() {
throw new Exception("Shared lib src/MyClass FOO=${System.getenv('FOO')")
}
}
** Run Result **
Jenkinsfile FOO=BAR
Shared lib var/lib FOO=BAR
java.lang.Exception: Shared lib src/MyClass FOO=null
...
It sure looks like the only way to handle this is to pass the this from Jenkins file down to the var/lib.groovy and harvest from that object
Jenkinsfile
withEnv(["FOO=BAR2"]) {
library 'my-shared-jenkins-library'
lib.displayEnv(this)
var/lib.groovy
def displayEnv(script) {
println "Shared lib var/lib FOO=${FOO}"
MyClass c = new MyClass(script)
}
src class
MyClass(def script) {
throw new Exception("FOO=${script.env.FOO}")
}
I believe you can populate the environment variable as below, where shared library can access.
Jenkisfile
env.FOO="BAR2"
library 'my-shared-jenkins-library'
lib()
vars/lib.groovy
def call(){
echo ("FOO: ${FOO}")
echo ("FOO:"+env.FOO)
}
Another method is use the "steps" variable:
In Jenkinsfile
mypackages.myclass.mymethod(steps)
In src
class myclass implements Serializable {
void mymethod(steps) {
String myEnvVar = steps.sh(returnStdout: true, script: "env | grep 'myVar' | cut -f 2- -d '='")
}
}
I stumbled upon this problem lately, so I'm gonna add my $0.02.
The basic template I use for var/*.groovy is:
// var/myMethod.groovy
import cool.package.Clazz
def call(Map m) {
m.put('env', env)
m.put('steps', steps)
new Clazz(m).call()
}
And the template for src/**/*.groovy
// src/cool/package/Clazz.groovy
class Clazz {
private String cool_field_1 = "default-value-1"
private int cool_value = 42
def env
def steps
def call() {
steps.echo("env.BUILD_TAG: ${env.BUILD_TAG}")
//...
}
}
In Jenkinsfile it is used standard way:
#Library('mylib#mybranch')
pipeline {
stages {
stage('St 1') {
steps { myMethod('cool_value': 43) }
}
}
}
Disclaimer: I don't do Groovy but since it looks similar to Java I can use it a little. Also using Map seems to give the advantage of quite flexible interface.
Not sure what the experts will say about the solution but I was able to access the variables defined in my Jenkinsfile from the shared library using evaluate.
Jenkinsfile
myVar = "abc"
vars/test.groovy
String myVar = evaluate("myVar")
For me this just works.
Jenkinsfile:
#Library('jenkins-library') _
pipeline {
agent any
environment {
FOO = 'bar'
}
stages {
stage('Build') {
steps {
script {
buildImage()
...
The library vars/buildImage.groovy:
def call() {
println(this.env.FOO)
println(env.FOO)
}
So to pass the environment to a class in the library, just use this in the vars/yourfunc.groovy.

How to capture a screenshot with PHPUnit and Selenium2 when the test fails?

I'm using PHPUnit 4.6 and PHPUnit Selenium 1.4.2 with PhantomJS. I want capture a screenshot with the last page when selenium test fails.
In PHPUnit Manual there is a example for Selenium 1, but I'm trying use with Selenium 2, because I need use GhostDriver.
WebTestCase.php
class WebTestCase extends PHPUnit_Extensions_Selenium2TestCase
{
protected $captureScreenshotOnFailure = TRUE;
protected $screenshotPath = '/../../screenshots';
protected $screenshotUrl = 'http://localhost:8080/screenshots';
protected function setUp() {
$this->setBrowser('phantomjs');
$this->setBrowserUrl("http://localhost:8080");
$this->setHost('localhost');
}
}
Test.php
class Test extends WebTestCase
{
public function testTitle()
{
$this->url('');
assertEquals($this->title(), "My App");
}
}
But this not capture a screenshot.
$ vendor/bin/phpunit
PHPUnit 4.6-ge85198b by Sebastian Bergmann and contributors.
Configuration read from /MyApp/phpunit.xml
F
Time: 231 ms, Memory: 5.50Mb
There was 1 failure:
1) Test::testTitle
Failed asserting that two strings are equal.
--- Expected
+++ Actual
## ##
-''
+'My App'
/MyApp/tests/functional/Test.php:7
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
Hmm. The difference between SeleniumTestCase and Selenium2TestCase is not really good documented in the PHPUnit Manual. Also there is no clear separation and not enough usage examples for common cases on Selenium2.
$captureScreenshotOnFailure does not exist on
PHPUnit_Extensions_Selenium2TestCase.
Anyway, let's try putting this together:
<?php
class Test extends PHPUnit_Extensions_Selenium2TestCase
{
protected function setUp() {
$this->setBrowser('phantomjs');
$this->setBrowserUrl("http://localhost:8080");
$this->setHost('localhost');
}
public function testEnterText()
{
$this->url("/");
try {
$this->assertEquals($this->title(), "My App");
} catch (Exception $e) {
$this->screenshot( __DIR__.'/'.$this->getName().'-'.time(). '.png');
}
}
public function screenshot($file)
{
$filedata = $this->currentScreenshot();
file_put_contents($file, $filedata);
}
}
The try-catch-block: in the try part the assertion is done, if the assertion fails, the exception is caught. The catch-block gives us a chance to (grab details of the exception or re-throw it or) make a screenshot.
The main function is $this->currentScreenshot(), which was used in this test
https://github.com/giorgiosironi/phpunit-selenium/blob/master/Tests/Selenium2TestCaseTest.php#L733
ScreenshotListener
Please note that there is a ScreenshotListener around, which might be worth looking at:
https://github.com/giorgiosironi/phpunit-selenium/blob/master/PHPUnit/Extensions/Selenium2TestCase/ScreenshotListener.php
With usage example over at https://github.com/giorgiosironi/phpunit-selenium/blob/master/Tests/Selenium2TestCase/ScreenshotListenerTest.php
This might be a cleaner implementation to grab test failures and make shots.
Combining the solutions from #Jens A. Koch and #John Joseph, we get this:
<?php
class homepageTest extends PHPUnit_Extensions_Selenium2TestCase {
private $listener;
public function setUp() {
// Your screenshots will be saved in '/var/www/vhosts/screenshots/'
$screenshots_dir = '/var/www/vhosts/screenshots/';
$this->listener = new PHPUnit_Extensions_Selenium2TestCase_ScreenshotListener($screenshots_dir);
$this->setBrowser('firefox');
$this->setBrowserUrl('https://netbeans.org');
}
public function testNetbeansContainsHorses() {
$this->url('https://netbeans.org');
$this->assertContains('Equestrian', $this->title()); // Will fail on NetBeans page.
}
public function onNotSuccessfulTest($e) {
$this->listener->addError($this, $e, microtime(true));
parent::onNotSuccessfulTest($e);
}
}
A way of doing this across all your web tests is to override one of the test failure functions from the parent test case class, and capture your screenshot there.
Example:
class MyBaseWebTests
{
$this->directory = '/some_path_to_put_screenshots_in/';
// Override PHPUnit_Extensions_Selenium2TestCase::onNotSuccessfulTest
public function onNotSuccessfulTest(Exception $e)
{
$filedata = $this->currentScreenshot();
$file = $this->directory . get_class($this) . '.png';
file_put_contents($file, $filedata);
parent::onNotSuccessfulTest($e);
}
}
Now, after any of your web tests fail, they will dump a screenshot in that folder with the name of the web test class as the filename.
Use this to save screenshot..very useful in case of headless browser.
$fp = fopen('path/35.png', 'wb');
fwrite($fp, $this->currentScreenshot());
fclose($fp);
sleep(1);

Drop-in packages in Go?

How would I go about having a package register some object (for instance a function) to a registry at load time such that adding a new package to the program will automatically add new functionality to the program without having to modify code in other packages?
Here's a code sample which should illustrate what I'm trying to do.
src/say/say.go:
package main
import (
"os"
"reg"
)
func main() {
if len(os.Args) != 2 {
os.Stderr.WriteString("usage:\n say <what_to_say>\n")
os.Exit(1)
}
cmd, ok := reg.GetFunc(os.Args[1])
if ok {
os.Stdout.WriteString(cmd())
os.Stdout.Write([]byte{'\n'})
} else {
os.Stderr.WriteString("I can't say that!\n")
os.Exit(1)
}
}
src/reg/reg.go:
package reg
var registry = make(map[string]func() string)
func Register(name string, f func() string) {
registry[name] = f
}
func GetFunc(name string) (func() string, bool) {
f, ok := registry[name]
return f, ok
}
src/hi/hi.go:
package hi
import (
"reg"
}
func init() {
reg.Register("hi", func() string {
return "Hello there!"
})
}
When coding this up, I naively supposed that perhaps the package "hi" would be found by the go compiler and compiled into the binary. Then, at load time, the init() function would run. If that was how things worked, I'd have been able to drop in something like the following to add a new "say no" command:
src/no/no.go:
package no
import (
"reg"
)
func init() {
reg.Register("no", func() string {
return "Not a chance, bub."
})
}
But, it doesn't seem to work that way.
I may just be thinking about the problem too much through a Pythonic lens, but is there some way to accomplish something somewhat like what I'm shooting for? If not, I'll change my tack and I will have learned something new about the Go way of doing things.
Thanks in advance!
Since you must use import in order for the compiler add a package, my suggestion would be to do the following:
Instead of using multiple drop-in packages, you could have only one single package with multiple drop-in files. Each command file is placed in the same package folder (cmds). This is possible since you are allowed to have multiple init in a package, and you would not have to make any edits to say.go, no matter how many new drop-in files you add.
package main
import (
"os"
"reg"
_ "cmds"
)
....
And previous package no
// Command no
package cmds
import (
"reg"
)
func init() {
reg.Register("no", func() string {
return "Not a chance, bub."
})
}
Based on what I read about the init function, I think your example would work if you just added "hi" and "no" to the list of packages you are importing in say.go. Does it work if you do that?
I know you did not want to change the code in say.go, so I suppose that isn't really a solution.