hebertialmeida/ModelGen

Order of properties arbitrary?

Sajjon opened this issue · 6 comments

The order of the properties is a bit strange. The order between the spec and the generated model is not the same. This is a bit problematic since we sometimes want to specify orders of the properties in our generated Swift files and the only way o

template.stencil

{% for p in spec.properties %}
    var {{ p.name }}: {{ p.type }} { get }
{% endfor %}
"properties": {
     "a": {"type": "integer"}
}

is trivial of course and results in:

var a: Int { get }

When we add a new property b below/after a, we expect b to be declared after a in the generated Swift file:

"properties": {
     "a": {"type": "integer"},
     "b": {"type": "integer"}
}

However, it results in the unexpected:

    var b: Int { get }
    var a: Int { get }

Hmm... "So maybe the order is reversed?" I thought, so continuing with a third property c:

"properties": {
      "a": {"type": "integer"},
      "b": {"type": "integer"},
      "c": {"type": "integer"}
}

Now it gets really interesting, this results in the super unexpected Swift code:

    var b: Int { get }
    var a: Int { get }
    var c: Int { get }

"So what if the order is completely random?" I thought, so I cleared the contents of the file and ran ModelGen like ten times. Same result, same order every time.

So what if we change a property, from type integer to string, what happens?

"properties": {
    "a": {"type": "integer"},
    "b": {"type": "string"},
    "c": {"type": "integer"}
}

The order did not change, still the unexpected and unwanted b, a, c:

    var b: String { get }
    var a: Int { get }
    var c: Int { get }

Adding a forth, d:

"properties": {
        "a": {"type": "integer"},
        "b": {"type": "string"},
        "c": {"type": "integer"},
        "d": {"type": "integer"},
}

Results in the unexpected:

    var b: String { get }
    var a: Int { get }
    var d: Int { get }
    var c: Int { get }

Adding a fifth e:

"properties": {
    "a": {"type": "integer"},
    "b": {"type": "string"},
    "c": {"type": "integer"},
    "d": {"type": "integer"},
    "e": {"type": "integer"}
}

Results in:

protocol RemoveThisModelTestingOnly {
    var b: String { get }
    var e: Int { get }
    var a: Int { get }
    var d: Int { get }
    var c: Int { get }
}

So the weird thing is that the same order seems to be occurring every time, but the order itself is completely arbitrary? At least I can see a pattern:

a
b, a
b, a, c
b, a, d, c
b, e, a, d, c

What is the cause of this? Because in JsonParser+Context it seems to be keeping the order in the method dicToArray:

	private func dicToArray() throws {
		guard let items = json["properties"] as? JSON else {
			throw JsonParserError.missingProperties
		}

		var properties = [JSON]()
		for (key, value) in items {
			guard let value = value as? JSON else {
				throw JsonParserError.missingProperties
			}
			var nValue = value
			nValue["name"] = key
			properties.append(nValue)
		}
		json["properties"] = properties
	}

The functions func mapProperties() and func prepareProperties(_ items: [JSON], language: Language) also seem to be keeping the order?

I struggled with this for a while because I wanted to render in the same order. If I remember that correct the problem is that the language will never preserve the order of a dictionary, I really wanted to do that but to be honest it is not something that we need because the order doesn't really matter while using it. So I just stopped working on that.

I think the order is missed when going to the stencil template.

@hebertialmeida maybe you could try to point me in the right direction? I posted some code snippets above and in those, I just saw arrays (var properties = [JSON]()), where in the code are the properties stored in a dictionary?

What we could do at least is to replace the arbitrary ordering to be alphabetically ordered. In the function private func prepareContextFor(_ language: Language) throws { we see this assignment:
json["properties"] = elements where elements is an array [JSON] so we could sort that on the property name, then the ordering is at least a bit logical.

What do you think, should work?

First thing is that the properties inside the template.stencil is a dictionary when swift reads that file seems that it is not preserving the order.

One place that may work ordering is dicToArray, because is where I convert the dictionary parsed from template to an array.

File: JsonParser+Context.swift

private func dicToArray() throws {

This should be fixed now with 1ded5fc that alphabetically order it. Please @Sajjon let me know what do you think.

@hebertialmeida cool! It has been some time since I used ModelGen, but it is very cool indeed! I might revisit it.

Btw are you aware that you almost managed to follow up EXACTLY one year after :D :D missed it by a day :P

Yeah, sorry about that, I use that on my project but sorting wasn't a problem, but since I upgrade to new swift probably dictionaries have changed it's behaviour and somehow was a mess...