Dynamic member lookup (via marker protocol)
This commit is contained in:
committed by
David Jennes
parent
203510175f
commit
7247d0a83d
15
Sources/DynamicMemberLookup.swift
Normal file
15
Sources/DynamicMemberLookup.swift
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/// Marker protocol so we can know which types support `@dynamicMemberLookup`. Add this to your own types that support
|
||||||
|
/// lookup by String.
|
||||||
|
public protocol DynamicMemberLookup {
|
||||||
|
/// Get a value for a given `String` key
|
||||||
|
subscript(dynamicMember member: String) -> Any? { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension DynamicMemberLookup where Self: RawRepresentable {
|
||||||
|
subscript(dynamicMember member: String) -> Any? {
|
||||||
|
switch member {
|
||||||
|
case "rawValue": return rawValue
|
||||||
|
default: return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -110,6 +110,8 @@ public struct Variable: Equatable, Resolvable {
|
|||||||
return object.value(forKey: bit)
|
return object.value(forKey: bit)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
} else if let value = context as? DynamicMemberLookup {
|
||||||
|
return value[dynamicMember: bit]
|
||||||
} else if let value = context {
|
} else if let value = context {
|
||||||
return Mirror(reflecting: value).getValue(for: bit)
|
return Mirror(reflecting: value).getValue(for: bit)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,17 @@ private class Blog: WebSite {
|
|||||||
let featuring: Article? = Article(author: Person(name: "Jhon"))
|
let featuring: Article? = Article(author: Person(name: "Jhon"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@dynamicMemberLookup
|
||||||
|
private struct DynamicStruct: DynamicMemberLookup {
|
||||||
|
subscript(dynamicMember member: String) -> Any? {
|
||||||
|
member == "test" ? "this is a dynamic response" : nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum DynamicEnum: String, DynamicMemberLookup {
|
||||||
|
case someValue = "this is raw value"
|
||||||
|
}
|
||||||
|
|
||||||
final class VariableTests: XCTestCase {
|
final class VariableTests: XCTestCase {
|
||||||
let context: Context = {
|
let context: Context = {
|
||||||
let ext = Extension()
|
let ext = Extension()
|
||||||
@@ -49,7 +60,11 @@ final class VariableTests: XCTestCase {
|
|||||||
],
|
],
|
||||||
"article": Article(author: Person(name: "Kyle")),
|
"article": Article(author: Person(name: "Kyle")),
|
||||||
"blog": Blog(),
|
"blog": Blog(),
|
||||||
"tuple": (one: 1, two: 2)
|
"tuple": (one: 1, two: 2),
|
||||||
|
"dynamic": [
|
||||||
|
"enum": DynamicEnum.someValue,
|
||||||
|
"struct": DynamicStruct()
|
||||||
|
]
|
||||||
], environment: environment)
|
], environment: environment)
|
||||||
#if os(OSX)
|
#if os(OSX)
|
||||||
context["object"] = Object()
|
context["object"] = Object()
|
||||||
@@ -158,6 +173,20 @@ final class VariableTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testDynamicMemberLookup() {
|
||||||
|
it("can resolve dynamic member lookup") {
|
||||||
|
let variable = Variable("dynamic.struct.test")
|
||||||
|
let result = try variable.resolve(self.context) as? String
|
||||||
|
try expect(result) == "this is a dynamic response"
|
||||||
|
}
|
||||||
|
|
||||||
|
it("can resolve dynamic enum rawValue") {
|
||||||
|
let variable = Variable("dynamic.enum.rawValue")
|
||||||
|
let result = try variable.resolve(self.context) as? String
|
||||||
|
try expect(result) == "this is raw value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testReflection() {
|
func testReflection() {
|
||||||
it("can resolve a property with reflection") {
|
it("can resolve a property with reflection") {
|
||||||
let variable = Variable("article.author.name")
|
let variable = Variable("article.author.name")
|
||||||
|
|||||||
Reference in New Issue
Block a user