New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
unbox #31
Comments
I would also like to combine this feature in some way with Vapor's validators |
This is interesting. I really like the cleaner syntax. Let's revisit this when Fluent is in a more functional state. The underpinnings of how everything will work are still not really decided. |
@tannernelson I was already really excited to start working on this, but okay, I'll hold myself. :-p |
Fluent is going to be top priority this next week, so expect to see some good progress there soon. :) |
I like this a lot actually. |
I think it'd be great to combine this with some rendition of Argo to make it really, really easy to make serialized objects. |
@NathanFlurry, could you show a code snippet of how that would make things even better than with only using Unbox? |
Let's take the example you provided. First of all, I'd probably rewrite it to be like this using Unbox, so I can reuse any extra code I write in my default initializer: struct User: Entity {
let id: Int?
let age: Int
let name: String
let gender: Gender
let birthday: NSDate
init(id: Int?, age: Int?, name: String, gender: Gender, birthday: NSDate) {
self.id = id
self.age = age ?? 18
self.name = name
self.gender = gender
self.birthday = birthday
}
init(unboxer: Unboxer) {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
self.init(
id: unboxer.unbox("id"),
age: unboxer.unbox("age"),
name: unboxer.unbox("age") ?? 18,
gender: unboxer.unbox("gender"),
birthday: unboxer.unbox("birthday", formatter: dateFormatter)
)
}
} With something like Argo (I'll just use their current DSL for this example, I'm not saying this is the best way to implement the API): struct User: Entity {
let id: Int?
let age: Int
let name: String
let gender: Gender
let birthday: NSDate
init(id: Int?, age: Int?, name: String, gender: Gender, birthday: NSDate) {
self.id = id
self.age = age ?? 18
self.name = name
self.gender = gender
self.birthday = birthday
}
static func decode(j: JSON) -> Decoded<User> {
return curry(User.init)
<^> j <| "id"
<*> j <| "age"
<*> j <| "name"
<*> j <| "gender"
<*> j <| "birthday"
}
} While there's not too much of a difference in the end, it's nice to not have to write things like |
I'm super biased because I have my own mapper, but if we're going to introduce a 3rd party mapper, I'd strongly prefer anything but Argo. It's concepts are excessively complex and difficult to work with. A large part of Genome above is focusing on super readable and clear error messages. It's also more customizable w/ transforms and utilizes the throwing system in a way that is easier in the long run. I think I'd prefer not to include 3rd party mappers in core if possible. If people really want one, I'd like to throw Genome into the evaluation mix. For ref: struct User: MappableObject {
let id: Int?
let age: Int
let name: String
let gender: Gender
let birthday: NSDate
init(map: Map) throws {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
id = try map.extract("id") // Since id is optional it will not fail when id is not present
age = try? map.extract("age") ?? 18 // Age is required, but I provided a fallback value so it won't fail
name = try map.extract("name") // As you can see it automatically returns the correct type
gender = try map.extract("gender") // And it even works for more complex types like enums
birthday = try map.extract("birthday", transform: dateFormatter.dateFromString)
}
} |
I prefer
Oh and |
I agree that it's less cluttered, but I'm very hesitant to use a mapper that's going to crash at runtime which unbox seems like it would have to do since it's not implemented as throwing. I think this is why mappers should maybe stay out of Fluent where possible. People have very different opinions on them (most of which are valid), probably why there's so many. The other issue I'm concerned about w/ unbox is that it doesn't seem to have a way to go Model => Data. if we were to include a default mapper, being able to serialize both ways with one syntax is ideal. |
Ah but that's where you don't fully understand struct User: Entity {
let id: Int?
let age: Int
let name: String
init(unboxer: Unboxer) {
id = unboxer.unbox("id")
age = unboxer.unbox("age")
name = unboxer.unbox("name")
}
} Let's say that Do you see that this has a huge advantage? Namely that unbox will gather everything that went wrong, not just the first thing. In your
Indeed, In Swift 4, when generics are hopefully finally completed and so subscripts will be generic, it can even become as beautiful as this: struct User: Entity {
let id: Int?
let age: Int
let name: String
init(box: Box) {
id = box["id"]
age = box["age"]
name = box["name"]
}
} |
Can't wait for full generic support either, that's definitely going to make all the mappers a lot more awesome. Especially when generic extensions can conform to protocols. That's going to allow a lot fewer overloads to support different generic scenarios! To your example, Genome would be: struct User: MappableObject {
let id: Int?
let age: Int
let name: String
init(map: Map) throws {
id = try box["id"]
age = try box["age"]
name = try box["name"]
}
} I understand your persistence, I really love the syntax of Genome, and a lot of people really love the syntax for Argo 😄 I disagree with a lot of the design decisions of Unbox, and don't see it being customized in a way that makes it worthwhile to fork. (I'm sure he disagrees with some of mine as well which is why he built it 😛 ) For now, I'd say people should build support for the mappers they prefer and our organization should stay out of it. I don't see a way to make everyone happy here. |
@loganwright I wonder if you read this part of what I wrote:
Because I'm very interested to hear what you think about it. Do you see it as an advantage? Or do you see it as bad design? |
We have this now with |
After watching the video and looking at the readme in the repository I'm convinced that Unbox could be a welcome addition to Fluent.
Unbox is a little library that unboxes any object from dictionary data with very simply syntax:
So instead of requiring
Entity
objects to implement aninit(serialized: [String: Value])
, we would just inherit from theUnboxable
protocol and requireEntity
objects to implementinit(unboxer: Unboxer)
.The code just looks so much cleaner. It would turn this:
Into this:
I think it is 100x nicer to read. So, what do you guys think?
The same guy also made a Wrap library which basically does the inverse of Unbox. If we would implement Wrap too then that would satisfy the other issue I opened #30
The text was updated successfully, but these errors were encountered: