RxKotlin combine latest more than 9 fields - rx-kotlin

I am new to Android development, I'm trying to make form validation using RxKotlin and RxBinding.
I need a guidance how to make form validation with more than 9 fields?
Actually I can combine the result using Observable.combinelatest.
This is the code I've been trying:
Observable.combineLatest(profileObserver, shopName, shopAddress, ownerName, idCard, ownerHp, ownerEmail, pin, confirmPin,
Function9<CharSequence, CharSequence, CharSequence, CharSequence, CharSequence, CharSequence, CharSequence, CharSequence, CharSequence,
Boolean> {profile, name, address, owner, card, hpNumber, email, currentPin, confirmationPin ->
return#Function9 isShopNameValid(name.toString()) && isShopAddressValid(address.toString())
&& isOwnerNameValid(owner.toString())
&& isIdCardValid(card.toString())
&& isOwnerHpValid(hpNumber.toString())
&& isOwnerEmail(email.toString())
&& isPinValid(currentPin.toString())
&& isConfirmPinValid(confirmationPin.toString())
}).subscribe {
registrationProcess.isEnabled = it
}
I still have 3 more fields to be validated.

there is a Observable.combineLatest method that takes an Iterable of observables for this case. Make a list of all of your form field observables and use that version of combineLatest.
val fieldsList = listOf(shopName) //etc
Observable.combineLatest(fieldsList){arrayOfLatest ->
//validate fields
isValidShopName(arrayOfLatest[0])// etc
}
I would also consider having each observable validate itself in this case. Example
shopName.map{
isValidShopName(it)
}
//and so on for each field
//Now combine into list as in above example
Observable.combineLatest(validatedFieldsList){arrayOfResults ->
arrayOfResults.any{!it} //If true there is an invalid field
}

Related

How can I change the Table class names using the by extending jOOQ's DefaultGeneratorStrategy?

I am using the jooq codgen gradle plugin to change the naming convention for generated tables to include Table at the end of the class name. However, I am not sure how to distinguish a table from a schema in the generator.
My naming override is:
#Override
public String getJavaClassName(Definition definition, Mode mode) {
String result = super.getJavaClassName(definition, mode);
if (mode == Mode.DEFAULT) {
result += "Table";
}
return result;
}
Is there a way to determine If the current object extends TableImpl or maybe I need to take a different approach?
Just use instanceof checks on your definition, like this:
#Override
public String getJavaClassName(Definition definition, Mode mode) {
String result = super.getJavaClassName(definition, mode);
if (mode == Mode.DEFAULT && definition instanceof TableDefinition) {
result += "Table";
}
return result;
}

have completion for #:op(a.b)

The #:op(a.b) feature is described here: https://haxe.io/releases/3.3.0/
I have May<T> abstract which is used for null safety. Here is the simplified version of it:
package;
import haxe.macro.Expr;
abstract May<T>(Null<T>) from(Null<T>){
// convert T, Null<T> or May<T> to May<T>
// accepts Null<T because of 'from(Null<T>)'
// accepts T because Null<T> has 'from(T)'
// we need to accept May<T> to avoid returning May<May<T>> from resolve() if field is already a May
public static inline function from<T>(t:May<T>):May<T> return t;
public inline function exist():Bool return this != null;
public inline function unwrap():T return exist() ? unsafeUnwrap() : throw 'unwrap null';
public inline function unwrapOr(defaultValue:T):T return exist() ? unsafeUnwrap() : defaultValue;
public inline function unsafeUnwrap():T return this;
// return same field from underlying type but as May<FieldType>
#:op(a.b) static macro function resolve<T>(ethis:ExprOf<May<T>>, name:String):Expr {
return macro {
var me = $ethis;
var result = May.from(me.exist() ? me.unsafeUnwrap().$name : null);
result;
}
}
}
Note the resolve() function. It's the new feature that I want to add to my actual May abstract. It allows to safely get fields from May and call unwrap() only once. For example:
may.exist() ? may.unwrap().someField : defaultValue
becomes
may.someField.unwrapOr(defaultValue)
That's very handy and works good. But the completion does not work. It only gives fields from May: unwrap(), exist() etc., but no fields from the underlying class.
I've decided to add #:forward metadata for completion:
#if display #:forward #end
This makes the compiler see all fields during completion. It's better than nothing, but fields have an incorrect type: T instead of May<T>, so I do not get completion for May fields.
I understand why the compiler can't know all possible fields when using #:op(a.b), but maybe there is some more clever trick that will help?

With Dapper Extensions, how do I sort by random?

It seems sorting in Dapper Extensions can be achieved with Predicates:
Predicates.Sort<Person>(p => p.LastName)
My question is, how do I implement random sorting (i.e. RAND() in sql) to predicates?
Predicates.Sort actually produces an ISort-compatible interface which is defined as follows:
public interface ISort
{
string PropertyName { get; set; }
bool Ascending { get; set; }
}
It looks like we have a chance of setting property name to "RAND()" or something, right?... But, sadly, this interface is used in this way:
if (sort != null && sort.Any())
{
sql.Append(" ORDER BY ")
.Append(sort.Select(s => GetColumnName(classMap, s.PropertyName, false) + (s.Ascending ? " ASC" : " DESC")).AppendStrings());
}
So Dapper Extensions in fact check that the passed name is a column name. And the GetColumnName is defined as follows:
public virtual string GetColumnName(IClassMapper map, string propertyName, bool includeAlias)
{
IPropertyMap propertyMap = map.Properties.SingleOrDefault(p => p.Name.Equals(propertyName, StringComparison.InvariantCultureIgnoreCase));
if (propertyMap == null)
{
throw new ArgumentException(string.Format("Could not find '{0}' in Mapping.", propertyName));
}
return GetColumnName(map, propertyMap, includeAlias);
}
Therefore, the string actually should be not a column name, but a property name (they have named the field in ISort interface for a purpose, right?).
So, to implement something like the thing you want you need to customize their SQL generator.
For further details, the best way is to refer to sources:
Predicates: https://github.com/tmsmith/Dapper-Extensions/blob/master/DapperExtensions/Predicates.cs
SqlGenerator: https://github.com/tmsmith/Dapper-Extensions/blob/master/DapperExtensions/Sql/SqlGenerator.cs
PS: I am unable to post link to the repo root due to beginner's rep, hope you can guess it ;)
PPS: The same is true for whole predicates system in Dapper Extensions. I believe it should be greatly refactored to allow more than plain-forward column-based restrictions.

How do I call a method of an attribute derived from a generic interface, where the specific type is not known?

Core Question:
I have a generic interface IValidatingAttribute<T>, which creates the contract bool IsValid(T value); The interface is implemented by a variety of Attributes, which all serve the purpose of determining if the current value of said Field or Property they decorate is valid per the interface spec that I'm dealing with. What I want to do is create a single validation method that will scan every field and property of the given model, and if that field or property has any attributes that implement IValidatingAttribute<T>, it should validate the value against each of those attributes. So, using reflection I have the sets of fields and properties, and within those sets I can get the list of attributes. How can I determine which attributes implement IValidatingAttribute and then call IsValid(T value)?
background:
I am working on a library project that will be used to develop a range of later projects against the interface for a common third party system. (BL Server, for those interested)
BL Server has a wide range of fairly arcane command structures that have varying validation requirements per command and parameter, and then it costs per transaction to call these commands, so one of the library requirements is to easily define the valdiation requirements at the model level to catch invalid commands before they are sent. It is also intended to aid in the development of later projects by allowing developers to catch invalid models without needing to set up the BL server connections.
Current Attempt:
Here's where I've gotten so far (IsValid is an extension method):
public interface IValidatingAttribute<T>
{
bool IsValid(T value);
}
public static bool IsValid<TObject>(this TObject sourceObject) where TObject : class, new()
{
var properties = typeof(TObject).GetProperties();
foreach (var prop in properties)
{
var attributeData = prop.GetCustomAttributesData();
foreach (var attribute in attributeData)
{
var attrType = attribute.AttributeType;
var interfaces = attrType.GetInterfaces().Where(inf => inf.IsGenericType).ToList();
if (interfaces.Any(infc => infc.Equals(typeof(IValidatingAttribute<>))))
{
var value = prop.GetValue(sourceObject);
//At this point, I know that the current attribute implements 'IValidatingAttribute<>', but I don't know what T is in that implementation.
//Also, I don't know what data type 'value' is, as it's currently boxed as an object.
//The underlying type to value will match the expected T in IValidatingAttribute.
//What I need is something like the line below:
if (!(attribute as IValidatingAttribute<T>).IsValid(value as T)) //I know this condition doesn't work, but it's what I'm trying to do.
{
return false;
}
}
}
return true;
}
}
Example usage:
Just to better explain what I am trying to achieve:
public class SomeBLRequestObject
{
/// <summary>
/// Required, only allows exactly 2 alpha characters.
/// </summary>
[MinCharacterCount(2), MaxCharacterCount(2), IsRequired, AllowedCharacterSet(CharSets.Alpha))]
public string StateCode {get; set;}
}
And then, later on in code:
...
var someBLObj = SomeBLRequestObjectFactory.Create();
if(!someBLObj.IsValid())
{
throw new InvalidObjectException("someBLObj is invalid!");
}
Thank you, I'm really looking for a solution to the problem as it stands, but I'm more than willing to listen if somebody has a viable alternative approach.
I'm trying to go generic extension method with this because there are literally hundreds of the BL Server objects, and I'm going with attributes because each of these objects can have upper double digit numbers of properties, and it's going to make things much, much easier if the requirements for each object are backed in and nice and readable for the next developer to have to use this thing.
Edit
Forgot to mention : This Question is the closest I've found, but what I really need are the contents of \\Do Something in TcKs's answer.
Well, after about 6 hours and a goods nights sleep, I realized that I was over-complicating this thing. Solved it with the following (ExtValidationInfo is the class that the below two extensions are in.):
Jon Skeet's answer over here pointed me at a better approach, although it still smells a bit, this one at least works.
public static bool IsValid<TObject>(this TObject sourceObject) where TObject : class, new()
{
var baseValidationMethod = typeof(ExtValidationInfo).GetMethod("ValidateProperty", BindingFlags.Static | BindingFlags.Public);
var properties = TypeDataHandler<TObject>.Properties;
foreach (var prop in properties)
{
var attributes = prop.GetCustomAttributes(typeof(IValidatingAttribute<>)).ToList();
if (!attributes.Any())
{
continue; // No validators, skip.
}
var propType = prop.PropertyType;
var validationMethod = baseValidationMethod.MakeGenericMethod(propType);
var propIsValid = validationMethod.Invoke(null, prop.GetValue(sourceObject), attributes);
if(!propIsValid)
{
return false;
}
}
return true;
}
public static bool ValidateProperty<TPropType>(TPropType value, List<IValidatingAttribute<TPropType>> validators)
{
foreach (var validator in validators)
{
if (!validator.IsValid(value))
{
return false;
}
}
return true;
}

RequestMapping on presence of one of multiple parameters

I have a Spring3 controller in which I'm using the #RequestMapping annotation. I know I can use the params value to route based on the the presence or lack of a url parameter, but is there a way to route based on the presence of one of two parameters?
Ideally I'd have something like the following:
#RequestMapping(value="/auth", params="error OR problem")
public ModelAndView errorInAuthenticate()
Where I route to errorInAuthenticate if the parameters error OR problem exist.
Unfortunately #RequestMapping params are combined using AND, not OR. (Source)
simply map both params as not required and test them:
#RequestMapping(value="/auth")
public ModelAndView errorInAuthenticate(#RequestParam(value="error", required=false) String errorParam,
#RequestParam(value="problem", required=false) String problemParam) {
if(errorParam != null || problemParam != null) {
//redirect
}
}
You can do it using Spring AOP and create a surrounding aspect for that request mapping.
Create an annotation like the following:
public #interface RequestParameterOrValidation{
String[] value() default {};
}
Then you can annotate your request mapping method with it:
#GetMapping("/test")
#RequestParameterOrValidation(value={"a", "b"})
public void test(
#RequestParam(value = "a", required = false) String a,
#RequestParam(value = "b", required = false) String b) {
// API code goes here...
}
Create an aspect around the annotation. Something like:
#Aspect
#Component
public class RequestParameterOrValidationAspect {
#Around("#annotation(x.y.z.RequestParameterOrValidation) && execution(public * *(..))")
public Object time(final ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args= joinPoint.getArgs();
MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getStaticPart().getSignature();
Method method = methodSignature.getMethod();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
RequestParameterOrValidation requestParamsOrValidation= method.getAnnotation(RequestParameterOrValidation.class);
String[] params=requestParamsOrValidation.value();
boolean isValid=false;
for (int argIndex = 0; argIndex < args.length; argIndex++) {
for (Annotation annotation : parameterAnnotations[argIndex]) {
if (!(annotation instanceof RequestParam))
continue;
RequestParam requestParam = (RequestParam) annotation;
if (Arrays.stream(params).anyMatch(requestParam.value()::equals) && args[argIndex]!=null) {
// Atleast one request param exist so its a valid value
return joinPoint.proceed();
}
}
}
throw new IllegalArgumentException("illegal request");
}
}
Note:- that it would be a good option to return 400 BAD REQUEST here since the request was not valid. Depends on the context, of course, but this is a general rule of thumb to start with.

Resources