pattern match args and give error messages in a lightweight Scala script - scripting

I write a number of simple scala scripts that end up starting with a simple pattern match on args like:
val Array(path, foo, whatever) = args
// .. rest of the script uses "path", "foo", etc.
Of course, if I supply the wrong number of arguments, I get an inscrutable error like:
scala.MatchError: [Ljava.lang.String;#7786df0f
at Main$$anon$1.<init>(FollowUsers.scala:5)
...
Is there an easy way to give a more useful error message? My current workaround is to do something like:
args match {
case Array(path, foo, whatever) => someFunction(path, foo, whatever)
case _ => System.err.println("usage: path foo whatever")
}
def someFunction(path: String, foo: String, whatever: String) = {
// .. rest of the script uses "path", "foo", etc.
}
But that feels like a lot of boilerplate what with having to define a whole other function, and having to repeat "path", "foo" and "whatever" in so many places. Is there a better way? I guess I could lose the function and put the body in the match statement, but that seems less readable to me.
I know I could use one of the many command line argument parsing packages, but I'm really looking for something extremely lightweight that I don't have to add a dependency and modify my classpath for.

How about?
val Array(path, foo, whatever) = if (args.length == 3) args
else throw new Exception("usage:path foo whatever")
==edit==
based on Randall's comment:
require(args.length == 3, "usage: path foo whatever")
val Array(path, foo, whatever) = args
That's minimum boilerplate. Your vals are in scope, you don't have to deal with closing brace and you get the usage error message.

scala> val args = Array("evil", "mad", "scientist")
args: Array[java.lang.String] = Array(evil, mad, scientist)
scala> def logToConsole(th: Throwable) { Console.err.println("Usage: path foo bar") }
logToConsole: (th: Throwable)Unit
scala> handling(classOf[MatchError]) by logToConsole apply {
| val Array(path, foo, bar) = args
| println(path)
| }
evil
scala> handling(classOf[MatchError]) by logToConsole apply {
| val Array(path, foo, bar) = Array("#fail")
| println(path)
| }
Usage: path foo bar

One way is to catch MatchError:
try {
val Array(path, foo, whatever) = args
} catch {
case _: MatchError => System.err.println("usage: path foo whatever")
}

Struck me that maybe the new util.control.Exception might have a solution:
import scala.util.control.Exception
Exception.handling(classOf[scala.MatchError]).by{
e => System.err.println("usage: path foo whatever")
} {
val Array(path, foo, whatever) = args
// .. rest of the script uses "path", "foo", etc.
}
This at least puts the error handling first and keeps the rest of the code together, though it makes me a little nervous to have such a large try block (that second block with the Array pattern matching is essentially all in the same try block being handled by Exception.handling).
EDIT: Looks like Missing Faktor posted about the same thing too, but with an explicitly defined function and an explicit call to apply.

Related

Unable to replace string inside a String in Kotlin

I am trying to replace a few sub strings inside a string. But my code doesn't seem to work.
val listOfMaleWords = listOf(" him", " he", " his")
val listOfFemaleWords = listOf(" her", " she", " her")
fun modifyIdeaForGender(rawIdea : String, desiredGender : String): String {
var theRawIdea = rawIdea
if (desiredGender == "FEMALE") {
println("desired gender is FEMALE")
listOfMaleWords.forEachIndexed { index, element ->
theRawIdea.replace(element, listOfFemaleWords[index])
}
} else {
println("desired gender is MALE")
listOfFemaleWords.forEachIndexed { index, element ->
theRawIdea.replace(element, listOfMaleWords[index])
}
}
return theRawIdea
}
fun main() {
var sampleString : String = "Tell him, he is special"
println(modifyIdeaForGender(sampleString, "FEMALE"))
}
Expected Output :
"Tell her, she is special"
Current Output :
"Tell him, he is special" // no change
Whats wrong with my code? The current output doesn't replace the string characters at all.
replace returns a new String that you are discarding immediately. It does not mutate theRawIdea itself, so you should assign it back to theRawIdea yourself. For example:
theRawIdea = theRawIdea.replace(element, listOfFemaleWords[index])
Though this would modify theRawIdea as you desire, it wouldn't replace the pronouns correctly. Once it replaces the "him"s with "her"s, it would try to replace the "he"s with "she"s. But note that "he" a substring of "her"! So this would produce:
Tell sher, she is special
This could be fixed by reordering the lists, putting the "he"-"she" pair first, or by using regex, adding \b word boundary anchors around the words:
// note that you should not have spaces before the words if you decide to use \b
val listOfMaleWords = listOf("him", "he", "his")
val listOfFemaleWords = listOf("her", "she", "her")
...
theRawIdea = theRawIdea.replace("\\b$element\\b".toRegex(), listOfFemaleWords[index])
Note that this doesn't account for capitalisation or the fact that changing from female gender pronouns to male ones is inherently broken. Your current code would change all her to him. It would require some more complicated natural language processing to accurately do this task in general.
Taking all that into account, I've rewritten your code with zip:
fun modifyMaleIdeaToFemaleGender(rawIdea : String): String {
var theRawIdea = rawIdea
// if you really want to do the broken female to male case, then this would be
// listOfFemaleWords zip listOfMaleWords
// and the loop below can stay the same
val zipped = listOfMaleWords zip listOfFemaleWords
zipped.forEach { (target, replacement) ->
theRawIdea = theRawIdea.replace("\\b$target\\b".toRegex(), replacement)
}
return theRawIdea
}
You can also use fold to avoid reassigning theRawIdea:
fun modifyIdeaToFemaleGender(rawIdea : String): String {
val zipped = listOfMaleWords zip listOfFemaleWords
return zipped.fold(rawIdea) { acc, (target, replacement) ->
acc.replace("\\b$target\\b".toRegex(), replacement)
}
}
Your code assumes that the replace() method performs an in-place mutation of the string. However, the string with the replaced values are returned by the replace(). So you need to change your code to contain something like:
theRawIdea = theRawIdea.replace(element, listOfFemaleWords[index])
To do this, you will have to use a conventional loop instead of listOfMaleWords.forEachIndexed style looping.

Is it possible to parameterize queries or parameters for an Acolyte ScalaCompositeHandler?

Background:
I have attempted to accomplish the question defined here, and I have not been able to succeed. Acolyte requires you to define the queries and parameters you want to handle within a match expression, and the values used in match expressions must be known at compile time. (Note, however, that this StackOverflow answer appears to provide a way around this limitation).
If this is indeed not possible, the inability to dynamically define the parameters and queries for Acolyte would be, for my use case, a severe limitation of the framework. I suspect this would be a limitation for others as well.
One SO user who has advocated for the use of Acolyte across a handful of questions stated in this comment that it is possible to dynamically define queries and their responses. So, I have opened this question as an invitation for someone to show that to be the case.
Question:
Using Acolyte, I want to be able to encapsulate the logic for matching queries and generating their responses. This is a desired feature because I want to keep my code DRY. In other words, I am looking for something like the following pseudo-code:
def generateHandler(query: String, accountId: Int, parameters: Seq[String]): ScalaCompositeHandler = AcolyteDSL.handleQuery {
parameters.foreach(p =>
// Tell the handler to handle this specific parameter
case acolyte.jdbc.QueryExecution(query, ExecutedParameter(accountId) :: ExecutedParameter(p) :: Nil) =>
someResultFunction(p)
)
}
Is this possible in Acolyte? If so, please provide an example.
It is indeed possible to parameterize queries and/or parameters by utilizing pattern matching.
See the code below for an example:
import java.sql.DriverManager
import acolyte.jdbc._
import acolyte.jdbc.Implicits._
import org.scalatest.FunSpec
class AcolyteTest extends FunSpec {
describe("Using pattern matching to extract a query parameter") {
it("should extract the parameter and make it usable for dynamic result returning") {
val query = "SELECT someresult FROM someDB WHERE id = ?"
val rows = RowLists.rowList1(classOf[String] -> "someresult")
val handlerName = "testOneHandler"
val handler = AcolyteDSL.handleQuery {
case acolyte.jdbc.QueryExecution(`query`, ExecutedParameter(id) :: _) =>
rows.append(id.toString)
}
Driver.register(handlerName, handler)
val connection = DriverManager.getConnection(s"jdbc:acolyte:anything-you-want?handler=$handlerName")
val preparedStatement = connection.prepareStatement(query)
preparedStatement.setString(1, "hello world")
val resultSet = preparedStatement.executeQuery()
resultSet.next()
assertResult(resultSet.getString(1))("hello world")
}
it("should support a slightly more complex example") {
val firstResult = "The first result"
val secondResult = "The second result"
val query = "SELECT someresult FROM someDB WHERE id = ?"
val rows = RowLists.rowList1(classOf[String] -> "someresult")
val results: Map[String, RowList1.Impl[String]] = Map(
"one" -> rows.append(firstResult),
"two" -> rows.append(secondResult)
)
def getResult(parameter: String): QueryResult = {
results.get(parameter) match {
case Some(row) => row.asResult()
case _ => acolyte.jdbc.QueryResult.Nil
}
}
val handlerName = "testTwoHandler"
val handler = AcolyteDSL.handleQuery {
case acolyte.jdbc.QueryExecution(`query`, ExecutedParameter(id) :: _) =>
getResult(id.toString)
}
Driver.register(handlerName, handler)
val connection = DriverManager.getConnection(s"jdbc:acolyte:anything-you-want?handler=$handlerName")
val preparedStatement = connection.prepareStatement(query)
preparedStatement.setString(1, "one")
val resultSetOne = preparedStatement.executeQuery()
resultSetOne.next()
assertResult(resultSetOne.getString(1))(firstResult)
preparedStatement.setString(1, "two")
val resultSetTwo = preparedStatement.executeQuery()
resultSetTwo.next()
assertResult(resultSetTwo.getString(1))(secondResult)
}
}
}

Kotlin String substitution not working when string is read from file

I have written a code that reads a text file. The text files contain placeholders which I would like to replace. The substitution does not work this way and the string is printed with the placeholders. Here is the code that I have written for this:
class TestSub(val sub: Sub) {
fun create() = template()
fun template() = Files.newBufferedReader(ClassPathResource(templateId.location).file.toPath()).readText()
}
data class Sub(val name: String, val age: Int)
Here is the main function that tries to print the final string:
fun main(args: Array<String>) {
val sub = Sub("Prashant", 32)
println(TestSub(sub).create())
}
However, when, instead of reading a file, I use a String, the following code works (Replacing fun template())
fun template() = "<h1>Hello ${sub.name}. Your age is ${sub.age}</h1>"
Is there a way to make string Substitution work when reading the content of a file?
Kotlin does not support String templates from files. I.e. code like "some variable: $variable" gets compiled to "some variable: " + variable. String templates are handled at compile time, which means it does not work with text loaded from files, or if you do something else to get the String escaped into a raw form. Either way, it would, as danielspaniol mentioned, be a security threat.
That leaves three options:
String.format(str)
MessageFormat.format(str)
Creating a custom engine
I don't know what your file contains, but if it's the String you used in the template function, change it to:
<h1>Hello {0}. Your age is {1,integer}</h1>
This is for MessageFormat, which is my personal preference. If you use String.format, use %s instead, and the other appropriate formats.
Now, use that in MessageFormat.format:
val result = MessageFormat.format(theString, name, age);
Note that if you use MessageFormat, you'll need to escape ' as ''. See this.
String substitution using ${...} is part of the string literals syntax and works roughly like this
val a = 1
val b = "abc ${a} def" // gets translated to something like val b = "abc " + a + " def"
So there is no way for this to work when you load from a text file. This would also be a huge security risk as it would allow for arbitrary code execution.
However I assume that Kotlin has something like a sprintf function where you can have placeholders like %s in your string and you can replace them with values
Take a look here. It looks like the easiest way is to use String.format
You are looking for something similar to Kotlin String templates for raw Strings, where placeholders like $var or ${var} are substituted by values, but this functionality needs to be available at runtime (for text read from files).
Methods like String.format(str) or MessageFormat.format(str) use other formats than the notation with the dollar prefix of Kotlin String templates. For "Kotlin-like" placeholder substitution you could use the function below (which I developed for similar reasons). It supports placeholders as $var or ${var} as well as dollar escaping by ${'$'}
/**
* Returns a String in which placeholders (e.g. $var or ${var}) are replaced by the specified values.
* This function can be used for resolving templates at RUNTIME (e.g. for templates read from files).
*
* Example:
* "\$var1\${var2}".resolve(mapOf("var1" to "VAL1", "var2" to "VAL2"))
* returns VAL1VAL2
*/
fun String.resolve(values: Map<String, String>): String {
val result = StringBuilder()
val matcherSimple = "\\$([a-zA-Z_][a-zA-Z_0-9]*)" // simple placeholder e.g. $var
val matcherWithBraces = "\\$\\{([a-zA-Z_][a-zA-Z_0-9]*)}" // placeholder within braces e.g. ${var}
// match a placeholder (like $var or ${var}) or ${'$'} (escaped dollar)
val allMatches = Regex("$matcherSimple|$matcherWithBraces|\\\$\\{'(\\\$)'}").findAll(this)
var position = 0
allMatches.forEach {
val range = it.range
val placeholder = this.substring(range)
val variableName = it.groups.filterNotNull()[1].value
val newText =
if ("\${'\$'}" == placeholder) "$"
else values[variableName] ?: throw IllegalArgumentException("Could not resolve placeholder $placeholder")
result.append(this.substring(position, range.start)).append(newText)
position = range.last + 1
}
result.append(this.substring(position))
return result.toString()
}
String templates only work for compile-time Sting literals, while what u read from a file is generated at runtime.
What u need is a template engine, which can render templates with variables or models at runtime.
For simple cases, String.format or MessageFormat.format in Java would work.
And for complex cases, check thymeleaf, velocity and so on.

How should I define these complex initializer for a property

Although I checked all tests in the kotlinpoet code, but I didn't find a proper way to implement below target codes, or I am not sure whether I used the best approach to do that. If anyone can provide some comments about this, that would be highly appreciate.
These properties are defined in the function of a class
Target Code 1
val outputState = StateType1(iouValue, ourIdentity, otherParty)
I used below codes to generate above code
.addCode(CodeBlock.of("%L",
PropertySpec.builder("outputState", ClassName("","StateType1"))
.initializer(CodeBlock.of("%T(%L, %L, %L)", ClassName("","StateType1"), "iouValue", "ourIdentity", "otherParty"))
.build()))
But question would be this outputState might be from different types, for example, StateType1 has 3 parameters, but StateTyp2 might only has 1 parameter, how should I dynamically define my kotlinpoet code to generate correct target code.
Target Code 2
val txBuilder = TransactionBuilder(notary = notary)
.addOutputState(outputState, TEMPLATE_CONTRACT_ID)
I didn't find a reference test case which has this scenario, after property's initializer then invoke it's function directly.
Use CodeBlock.Builder for the first example, it gives you more flexibility in constructing CodeBlocks:
fun createConstructorCall(type: TypeName, vararg args: String): CodeBlock {
val argsCode = args
.map { CodeBlock.of("%L", it) }
.joinToCode(separator = ", ", prefix = "(", suffix = ")")
return CodeBlock.Builder()
.add("%T", type)
.add(argsCode)
.build()
}
val className = ClassName("", "StateType1")
val codeBlock = CodeBlock.of("%L", PropertySpec.builder("outputState", className)
.initializer(createConstructorCall(className, "iouValue", "ourIdentity", "otherParty"))
.build())
assertThat(codeBlock.toString()).isEqualTo("""
|val outputState: StateType1 = StateType1(iouValue, ourIdentity, otherParty)
|""".trimMargin())
In the second example, we don't really provide anything special, pass your code as a String and feel free to use placeholders to parameterize if needed:
val className1 = ClassName("", "TransactionBuilder")
val codeBlock1 = CodeBlock.of("%L", PropertySpec.builder("txBuilder", className)
.initializer(
"%T(notary = notary)\n.addOutputState(outputState, TEMPLATE_CONTRACT_ID)",
className1)
.build())
assertThat(codeBlock1.toString()).isEqualTo("""
|val txBuilder: StateType1 = TransactionBuilder(notary = notary)
| .addOutputState(outputState, TEMPLATE_CONTRACT_ID)
|""".trimMargin())

Access Instance-property in Coffeescript within nested

so I am using express within a node-app. As my app is getting bigger I want to put my routes into extra files. I seem to be able to get hold of the bugDB if I just get rid of the intermediate get object. But I can't access the bugDB in the inner object. Any suggestions? Maybe there is even a more nice code pattern for how to accomplish this more elegantly.
I would appreachate your help. Thanks in advance. (As I am not a native speaker I couldn't find others with a similar problem, if you know how to phrase the question better, please show me the way :) )
BUGROUTER.COFFEE
class BugsRouter
constructor: (#bugDB)-> // instance-variable with databaselink
return
get:{
allBugs: (req, res)=>
console.log "db", #bugDB // this gives me undefined
// is "this" in the get context?
#bugDB.allDocs {include_docs: true}, (err, response)->
res.json 200, response
}
module.exports = BugsRouter
SERVER.COFFEE
BugsRouter = require "./routes/BUGROUTER"
bugsRouter = new BugsRouter(bugDB)
console.log bugsRouter.bugDB # this is working
app.get "/bugs/all", bugsRouter.get.allBugs
Sub-objects don't work like that. When you say this:
class C
p:
f: ->
Then p is just a plain object that happens to be a property on C's prototype, it will have no special idea of what # should be inside f. And if you try to use a fat-arrow instead:
class C
p:
f: =>
then you're accidentally creating a namespaced class function called f so # will be C when f is called. In either case, saying:
c = new C
c.p.f()
is the same as:
c = new C
p = c.p
p.f()
so f will be called in the context of p rather than c.
You can get around this if you don't mind manually binding the functions inside get when your constructor is called:
constructor: (#bugDB) ->
#get = { }
for name, func of #constructor::get
#get[name] = func.bind(#)
This assumes that you have Function.bind available. If you don't then you can use any of the other binding techniques (_.bind, $.proxy, ...). The #get = { } trick is needed to ensure that you don't accidentally modify the prototype's version of #get; if you're certain that you'll only be creating one instance of your BugsRouter then you could use this instead:
constructor: (#bugDB) ->
for name, func of #get
#get[name] = func.bind(#)
to bind the functions inside the prototype's version of get rather than the instance's local copy.
You can watch this simplified demo to see what's going on with # in various cases, keep an eye on the #flag values to see the accidental prototype modification caused by not using #get = { } and #constructor::get:
class C1
get:
f: -> console.log('C1', #)
class C2
get:
f: => console.log('C2', #)
class C3
constructor: ->
#flag = Math.random()
for name, func of #get
#get[name] = func.bind(#)
get:
f: -> console.log('C3', #)
class C4
constructor: ->
#flag = Math.random()
#get = { }
for name, func of #constructor::get
#get[name] = func.bind(#)
get:
f: -> console.log('C4', #)
for klass in [C1, C2, C3, C3, C4, C4]
o = new klass
o.get.f()
Live version of the above: http://jsfiddle.net/ambiguous/8XR7Z/
Hmm, seems like I found a better solution after all:
class Test
constructor: ->
#testVariable = "Have a nice"
return
Object.defineProperties #prototype,
get:
enumerable :true
get:->
{
day: => #testVariable + " day"
week: => #testVariable + " day"
}
console.log (new Test()).get.day()
This allows me to call (new Test()).get.day() the way I wanted.
Live version at: JSFiddle