I am looking for Guava Truth equivalent of AssertJ usingElementComparatorIgnoringFields to ignore some field.
Exemple:
data class CalendarEntity(
#PrimaryKey(autoGenerate = true)
var id: Int = 0,
var name: String
)
Truth.assertThat(currentCalendars).containsExactlyElementsIn(expectedCalendars) // Here I want to ignore the id field
Thanks for your help.
For Truth, we decided not to provide reflection-based APIs, so there's no built-in equivalent.
Our general approach to custom comparisons is Fuzzy Truth. In your case, that would look something like this (Java, untested):
Correspondence<CalendarEntity, CalendarEntity> ignoreId =
Correspondence.from(
(a, b) -> a.name.equals(b.name),
"fields other than ID");
assertThat(currentCalendars).usingCorrespondence(ignoreId).containsExactlyElementsIn(expectedCalendars);
If you anticipate wanting this a lot (and you want to stick with Truth rather than AssertJ), then you could generalize the ignoreId code to work with arbitrary field names.
(Also: In this specific example, your CalendarEntity has only one field that you do want to compare. In that case, you can construct the Correspondence in a slightly simpler way: Correspondence.transforming(CalendarEntity::name, "name").)
Related
This might be a very silly question, but I am logging the methods that are triggered in my app as strings. When an issue is submitted, I would like to automatically input the text of the strings as parameters for methods. E.g:
For method:
fun assignPot(potType: PotType, ball: DomainBall, action: PotAction) {...}
I'd like to somehow call method:
assignPot(FOUL(2, BLUE(5), SWITCH))
From String:
"FOUL(2, BLUE(5), SWITCH)"
The only workaround I can think of is to split the string and create a when -> then function to get actual classes from strings, but I wondered if there's a more concise way for this.
This is not what you want to do. You should design your app in a way that prevents users from providing input similar to actual code.
However, you can achieve this. Complex parsings like this oftenly use regex-based approaches.
As you said, you should map your string part to class. If your PotType is enum, you can do something like
val re = Regex("[^A-Za-z\s]")
val reNumbers = Regex("[^0-9\s]")
// get classes
val classNames = originalString.replace(re, "").split(" ")
// get their constructor numerical arguments
val classArgs = originalString.replace(reNumbers, "").split(" ")
After that you can implement mapping with when expression. You probably will use some collection of Any type.
As you see, this sadly leads you to parsing code by code. Concise way to solve is to implement your own script compiler/interpreter and use it in your application :) That will later lead you to dealing with security breaches and so on.
If you are logging problematic method calls and want to repeat them immediately after issue is submitted, you probably want to programatically save the calls to lambdas and call them when you receive an issue log.
It's often the case that uiState contains text coming from the resources.
As it's not advised to access the context in ViewModels, I used to resolve these strings in a view, by having the VM expose a wrapper class encapsulating a string resource and optional parameters. This class that was returning the resolved text once called with a context from a UI layer.
It's okay, but things get more complicated when we need to introduce plural strings or return a string that is constructed out of multiple other ones.
I just thought of a "one fits all" solution that would possibly help me with all the cases, but I have a got a gut feeling that it's too simple to be true. I haven't also seen anyone choosing such a way of addressing this problem.
typealias StringProvider = Context.() -> String
data class UiState(val text: StringProvider)
// in viewmodel
_state.update { UiState(text = { getString(R.string.test) + "extratext" } }
// in UI (in this case Compose)
Text(text = uiModel.text(LocalContext.current))
I'd like to ask you for an evaluation of this idea and for pointing out better alternatives in case this is not a valid approach.
I have this sample data class:
data class Car ( var id: String )
Now I can create a fun as this:
fun doWhatever(id: String){}
My problem is that if my customer then tells me that Id is an int, I have to change it in both places.
So what I want is to set Car.id type as refence in fun doWhatever, something like this:
fun doWhatever(id: propertyTypeOfCarId){}
So I if the customer changes type, I only have to change it in the class.
I read all kind of posts, but I wasnt able to find the answer. Any idea how to achieve it?
If this isn't something you expect to be doing regularly, consider just using the refactoring tools the IDE provides. You code to handle a specific set of data, and if the structure of that data changes, you have to adapt the code to fit it. Baking in a bunch of "what if" functionality can add complexity, compared to just saying a String is a String and changing it if it ever needs changing, using the tools provided to make that as quick and easy as possible.
But sometimes you need to do this kind of thing, and Kotlin has some nice language features it can be worth using, like type aliases:
typealias CarId = String
data class Car(var id: CarId)
fun doWhatever(id: CarId){}
Two benefits here: the actual type is only defined in one place, so you can change that String to an Int without needing to change anything else - except stuff that relied on the ID being a String specifically of course
The other benefit is you're actually adding some semantic information by using that very specific type. That function isn't supposed to just take any old String - it's specifically meant to handle CarIds. It tells you what it's for, and that can make your code a lot easier to understand
(The function will accept Strings, because CarId is just an alias - an alternative name for a String - so it's not a way to enforce structure on your code, just a way to make it nicer to read and write. You can't define a new type that is a String in Kotlin unfortunately)
If the number of id types you support is limited, you can simply use method overloading:
fun doWhatever(id: String){}
fun doWhatever(id: Int){}
// etc.
Alternatively, you can use a reified generic parameter in your method to support any number of types:
inline fun <reified T> doWhatever(id: T) {
when (T::class) {
Int::class -> {}
String::class -> {}
}
}
By default, Room creates a column for each field that is defined in the entity. If an entity has fields that I don't want to persist, I have to use the #Ignore annotation.
This poses a problem with inheritance. Annotating all the unwanted fields from a base class becomes unfeasible at a certain point, especially if you have to make your own versions of complex stock objects only to ignore the fields.
Currently, I am using interfaces instead of inheritance to work around that problem, but I would prefer to have a base class for my Room objects.
Do you know a way to ignore fields by default, so I can rather De-Ignore the desired fields instead of the other way around? Preferably in Kotlin?
Edit:
I want to build a treeview of different room entities and it would be nice to have my treeview item as a base class for all of them. But the treeview item implements a lot of stuff, it is not practical to customize all of that just for #Ignore tags. There are workarounds, but i would need less code if i do it this way.
You can use #Ignore on your base classes as well, for instance:
open class MyBaseClass{
#Ignore
open var somethingBasic: Int = 0
}
#Entity(...)
class A : MyBaseClass{
var name: String? = null
}
#Entity(...)
class B : MyBaseClass{
var type: Int = 0
}
But be careful about this because using a base class for different tables is a bit abnormal and it's able to break all your tables somewhere (take migrations as an example). I suggest to take a deeper look on your structure and try to stay away from this :D
I am trying to use the put method of the ContentValues class in the Android API from Scala, but I can't figure out a way to make Scala narrow down the type of list elements. For example :
val a: List[(String, Any)](...)
val cv = new ContentValues
for ((k,v) <- a) cv.put(k,v)
...yields an error that roughly says it can't resolve the "overloaded put method with alternatives put(String, String), put(String, Int),...".
My intuition is telling me I should probably use reflection, but I can't figure out how to do it exactly. Any pointer?
The type of your list is too imprecise. ContentValues.put has overloads for a number of types, but they are all specific (Integer, Float, etc.), and no alternative for Object.
You can:
make the list type more precise, in case the list has only elements of one type (say, Int).
use an HList, but beware of type wizardry. It may be overkill in this case
as a last resort, do a type test:
for ((k, v) <- a) v match {
case n: Int => cv.put(k, v)
case d: Double => cv.put(k, d)
//...
}
This is a very famous and common problem in Scala. The type system works correctly, and developers are simply not used to it.
In strongly typed programming languages the compiler verifies rules on the types you are using and this prevents painful runtime errors. If you use reflection or casts you are allowed to force the type system and this can produce unexpected errors and problems at runtime.
Why would you want to break the rules which you largely benefit from ? A good reason is when you are designing a library and you know exactly what is going to happen in your internals (see scala collections).
When you end up using types such as Any or AnyRef you should be worried and ask yourself:
why I am going so up in the hierarchy ?
Am I probably using an anti pattern or a bad constructor for a strongly typed programming language?
In fact I guess you are, and I propose we further investigate together why you have a List[(String,Any)]. Let's assume you had no problems in putting data into the ContentValues , what if later you would need to extract something from there? How would you know if it was an Int, a Double, a Float?