Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Own adapters #203

Closed
dcordero opened this Issue Dec 18, 2013 · 14 comments

Comments

Projects
None yet
4 participants
Contributor

dcordero commented Dec 18, 2013

I created my own adapter using the new "JSONKeyPathForPropertyKey" way. But I am having some problems since from the reversed transformer I can not know what Adapter was used for the main model. And I am forced to use the MTLJSONAdapter instead of mine, getting a different behaviour for the nested models.

So my proposal is sending the Adapter to the transformers, allowing get the wanted behaviour on demand

// MTLJSONAdapter.m
-value = [transformer reverseTransformedValue:value withAdapter:self] ?: NSNull.null;
+value = [transformer reverseTransformedValue:value] ?: NSNull.null;
// MTLValueTransformer
- - (id)reverseTransformedValue:(id)value {
-   return self.reverseBlock(value);
+ - (id)reverseTransformedValue:(id)value withAdapter:(MTLJSONAdapter *)adapter {
+   return self.reverseBlock(value, adapter);
// MTLValueTransformer.h
-typedef id (^MTLValueTransformerBlock)(id);
+typedef id (^MTLValueTransformerBlock)(id,MTLJSONAdapter *);
Owner

robb commented Dec 18, 2013

I am not sure that coupling MTLValueTransformer to MTLJSONAdapter is the way to go. What kind of logic would you have that changes based on the adapter? Have you considered using a different method for declaring the transformers, like so:

// XYModel.h

+ (NSValueTransformer *)propertyJSONTransformer {
    return …
}

+ (NSValueTransformer *)propertyCustomTransformer {
    return …
}

What is your underlying motive for the custom transformer, are you working with another API that has different constraints on the values?

Contributor

dcordero commented Dec 18, 2013

More than a custom transformer what I would like is use the same one but from different Adapters. Let me explain myself with an example:

Imagine I have this json:

{
   "key1" : "Hello World",
   "key2" : nil,
   "key3" : "Hola Mundo",
   "nestedModel" : [{
       "subkey1" : 12345,
       "subkey2" : nil,
       "subkey3" : "Hola Mundo"
   }]
}

And I model it with a couple of models. The first one with 4 properties for: key1, key2, key3, and nestedmodel. And this last one is an array of objects of another model with 3 keys: subkey1, subkey2 and subkey3

So, apart from the behaviour I get using MTLJSONAdapter that let me generate this json as it is using, "in addition" I would like to have another behaviour as well

The second behaviour I would like to have is if for example, create an Adapter that let me reverse-transform this model but I want to avoid having keys equals to "Hola Mundo" in the generated JSONDictionary. So, I can create my own Adapter using JSONKeyPathForPropertyKey as follow....

- (NSString *)JSONKeyPathForPropertyKey:(NSString *)propertyKey {

    id value = [self.model valueForKey:propertyKey];

    if ([value isKindOfClass:NSString.class] && [value isEqualToString:@"Hola Mundo"]){
        return [super JSONKeyPathForPropertyKey:propertyKey];

    }

    return nil;

}

And I get my JSONDictionary using:

NSDictionary *myReversedModel = [MyCustomJSONAdaper JSONDictionaryFromModel:myModel];

or

NSDictionary *myReversedModel = [MTLJSONAdapter JSONDictionaryFromModel:myModel];

depending on the behaviour I want in any moment.

With this situation... As the transformer of my nested my model is something like that in the main model:

+ (NSValueTransformer *)nestedModelJSONTransformer
{
    return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(id whatever) {
            [..... WHAT EVER ....]
    } reverseBlock:^(NSArray *nestedModels) {
        NSArray *result;
        if (nestedModels) {
            result = [[NSArray alloc] init];
            for (NestedModel *submodel in nestedModels) {
                NSDictionary *nestedModelReversed = [MTLJSONAdapter JSONDictionaryFromModel:submodel];
                if (nestedModelReversed) {
                    [result addObject:nestedModelReversed];
                }
            }
        }
        return result;
    }];
}

And in this reverse transformer I am forced to always use MTLJSONAdapter instead of my own adapter when the model is beeing transformer from a different Adapter. That is why I think it could be a good idea be able to know the adapter is currently doing the transformation sending it to the transformer.

Owner

robb commented Dec 18, 2013

So you're saying you'd need something like

  • (NSValueTransformer *)mtl_JSONDictionaryTransformerWithModelClass:(Class)modelClass;

for arbitrary MTLJSONAdapter subclasses?

(Sent from my phone)

On Dec 18, 2013, at 1:51 PM, dcordero notifications@github.com wrote:

More than a custom transformer what I would like is use the same one but from different Adapters. Let me explain myself with an example:

Imagine I have this origin json:

{
"key1" : "Hello World",
"key2" : nil,
"key3" : "Hola Mundo",
"nestedModel" : [{
"subkey1" : 12345,
"subkey2" : nil,
"subkey3" : "Hola Mundo"
}]
}

And I model it with a couple of models. The first one with 4 properties for: key1, key2, key3, and nestedmodel. And this last one is an array of objects of another model with 3 keys: subkey1, subkey2 and subkey3

So, apart from the behaviour I get using MTLJSONAdapter that let me generate this json as it is using, "in addition" I would like to have another behaviour as well

The second behaviour I would like to have is if for example, create an Adapter that let me reverse-transform this model but I want to avoid having keys equals to "Hola Mundo" in the generated JSONDictionary. So, I can create my own Adapter using JSONKeyPathForPropertyKey as follow....

  • (NSString *)JSONKeyPathForPropertyKey:(NSString *)propertyKey {

    id value = [self.model valueForKey:propertyKey];

    if ([value isKindOfClass:NSString.class] && [value isEqualToString:@"Hola Mundo"]){
    return [super JSONKeyPathForPropertyKey:propertyKey];

    }

    return nil;

}
And I get my JSONDictionary using:

NSDictionary *myReversedModel = [MyCustomJSONAdaper JSONDictionaryFromModel:myModel];
or

NSDictionary *myReversedModel = [MTLJSONAdapter JSONDictionaryFromModel:myModel];
depending on the behaviour I want in any moment.

With this situation... As the transformer of my nested my model is something like that in the main model:

  • (NSValueTransformer *)nestedModelJSONTransformer
    {
    return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(id whatever) {
    [..... WHAT EVER ....]
    } reverseBlock:^(NSArray *nestedModels) {
    NSArray *result;
    if (nestedModels) {
    result = [[NSArray alloc] init];
    for (NestedModel *submodel in nestedModels) {
    NSDictionary *nestedModelReversed = [MTLJSONAdapter JSONDictionaryFromModel:submodel];
    if (nestedModelReversed) {
    [result addObject:nestedModelReversed];
    }
    }
    }
    return result;
    }];
    }
    And in this reverse transformer I am forced to always use MTLJSONAdapter instead of my own adapter when the model is beeing transformer from a different Adapter. That is why I think it could be a good idea be able to know the adapter is currently doing the transformation sending it to the transformer.


Reply to this email directly or view it on GitHub.

Owner

robb commented Mar 4, 2014

Did you figure this out? :-)

Member

dcaunt commented Mar 4, 2014

@robb I guess it would be useful to be able to specify the MTLJSONAdapter subclass to use in mtl_JSONDictionaryTransformerWithModelClass: (and mtl_JSONArrayTransformerWithModelClass:)

Owner

robb commented Mar 4, 2014

@dcaunt Yeah, I guess we should expect MTLJSONAdapters to be subclassed post 2.0.

Alternatively, I was thinking we could also make it a class method on the adapter

@interface MTLJSONAdapter (NSValueTransformer)

+ (NSValueTransformer *)arrayTransformerWithModelClass:(Class)class;
+ (NSValueTransformer *)dictionaryTransformerWithModelClass:(Class)class;

@end

that way the class of the adapter would be implied and subclasses wouldn't even have to care. On the other hand, there's something to be said for keeping everything in the NSValueTransformer category.

Member

dcaunt commented Mar 4, 2014

The predefined transformers could call the class methods on MTLJSONAdapter giving you Mantle's default behaviour. Calling either of the class methods on an adapter subclass yields an instance of the subclass.

Owner

robb commented Mar 4, 2014

The predefined transformers could call the class methods on MTLJSONAdapter giving you Mantle's default behaviour.

This would be needed for backwards compatibiliy, but I'd rather only have

@interface NSValueTransformer (PredefinedTransformers)

+ (NSValueTransformer *)mtl_JSONArrayTransformerWithModelClass:(Class)class __attribute((deprecated));
+ (NSValueTransformer *)mtl_JSONDictionaryTransformerWithModelClass:(Class)class __attribute((deprecated));

@end

@interface MTLJSONAdapter (NSValueTransformer)

+ (NSValueTransformer *)arrayTransformerWithModelClass:(Class)class;
+ (NSValueTransformer *)dictionaryTransformerWithModelClass:(Class)class;

@end

OR

@interface NSValueTransformer (PredefinedTransformers)

+ (NSValueTransformer *)mtl_JSONArrayTransformerWithModelClass:(Class)class;
+ (NSValueTransformer *)mtl_JSONArrayTransformerWithModelClass:(Class)class JSONAdapterClass:(Class)adapterClass;

+ (NSValueTransformer *)mtl_JSONDictionaryTransformerWithModelClass:(Class)class;
+ (NSValueTransformer *)mtl_JSONDictionaryTransformerWithModelClass:(Class)class JSONAdapterClass:(Class)adapterClass;

@end

That being said, I don't think we'll have a common superclass or protocol for the adapters any time soon, so +arrayTransformerWithModelClass:JSONAdapterClass: could only take MTLJSONAdapters subclasses and it seems pointless to have that method if there is no other viable class shipping with Mantle…

Owner

robb commented Mar 4, 2014

FWIW, I think the MTLJSONAdapter (NSValueTransformer) pattern would also scale better for future and external adapters.

Member

dcaunt commented Mar 4, 2014

Agreed, the first option looks much better to me. The transformers are integral to JSON adaptation and I think it's clearer to group them in this way. Otherwise, you're right about the issue of scale - there would be lots of similarly named mtl_ category methods.

@robb robb added the enhancement label Mar 4, 2014

Member

dcaunt commented Mar 4, 2014

@robb Do you want to implement this against 2.0-development or master?

Owner

jspahrsummers commented Mar 4, 2014

👍 on moving these methods to MTLJSONAdapter (against 2.0-development).

Member

dcaunt commented Mar 4, 2014

👍

On Tuesday, 4 March 2014, Justin Spahr-Summers notifications@github.com
wrote:

[image: 👍] on moving these methods to MTLJSONAdapter (against
2.0-development).


Reply to this email directly or view it on GitHubhttps://github.com/MantleFramework/Mantle/issues/203#issuecomment-36682087
.

@robb robb added the JSON label Mar 6, 2014

@jspahrsummers jspahrsummers added this to the 2.0 milestone Mar 16, 2015

@jspahrsummers jspahrsummers self-assigned this Mar 16, 2015

Owner

robb commented Mar 17, 2015

Closed by #474

@robb robb closed this Mar 17, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment