1677700380
Using Schemats, you can generate TypeScript interface definitions from (Postgres, MySQL) SQL database schema automatically.
Start with a database schema:
Users | |
---|---|
id | SERIAL |
username | VARCHAR |
password | VARCHAR |
last_logon | TIMESTAMP |
Automatically have the following TypesScript Interface generated
interface Users {
id: number;
username: string;
password: string;
last_logon: Date;
}
For an overview on the motivation and rational behind this project, please take a look at Statically typed PostgreSQL queries in Typescript .
npm install -g schemats
schemats generate -c postgres://postgres@localhost/osm -t users -o osm.ts
schemats generate -c mysql://mysql@localhost/osm -t users -o osm.ts
The above commands will generate typescript interfaces for osm
database with table users
. The resulting file is stored as osm.ts
.
To generate all type definitions for all the tables within the schema 'public':
Note: MySQL does not have a default public schema, but should it have a schema named public, this will still work.
schemats generate -c postgres://postgres@localhost/osm -s public -o osm.ts
schemats generate -c mysql://mysql@localhost/osm -s public -o osm.ts
If neither the table parameter nor the schema parameter is provided, all tables in schema 'public' will be generated, so the command above is equivalent to:
schemats generate -c postgres://postgres@localhost/osm -o osm.ts
schemats generate -c mysql://mysql@localhost/osm -o osm.ts
Schemats supports reading configuration from a json config file (defaults to schemats.json
). Instead of passing configuration via commandline parameter like done above, it is also possible to supply the configuration through a config file. The config file supports the same parameters as the commandline arguments.
For example, if a schemats.json
exists in the current working directory with the following content:
{
"conn": "postgres://postgres@localhost/osm",
"table": ["users"]
}
Running schemats generate
here is equivalent to running schemats generate -c postgres://postgres@localhost/osm -t users -o osm.ts
.
We can import osm.ts
directly
// imports the _osm_ namespace from ./osm.ts
import * as osm from './osm'
// Now query with pg-promise and have a completely typed return value
let usersCreatedAfter2013: Array<osm.users>
= await db.query("SELECT * FROM users WHERE creation_time >= '2013-01-01'");
// We can decide to only get selected fields
let emailOfUsersCreatedAfter2013: Array<{
email: osm.users['email'],
creation_time: osm.users['creation_time']
}> = await db.query("SELECT (email, creation_time) FROM users WHERE creation_time >= '2013-01-01'");
With generated type definition for our database schema, we can write code with autocompletion and static type checks.
Schemats exposes two high-level functions for generating typescript definition from a database schema. They can be used by a build tool such as grunt and gulp.
Version 1.0 deprecates generating schema typescript files with namespace.
Instead of generating schema typescript files with
schemats generate -c postgres://postgres@localhost/db -n yournamespace -o db.ts
and import them with
import {yournamespace} from './db'
It is now encouraged to generate without namespace
schemats generate -c postgres://postgres@localhost/db -o db.ts
and import them with
import * as yournamespace from './db'
// or
import {table_a, table_b} from './db'
As TypeScript's documentation describes, having a top level namespace is needless. This was discussed in #25.
Generating schema typescript files with namespace still works in v1.0, but it is discouraged and subjected to removal in the future.
Version 1.0 supports strict null-checking and reflects the NOT NULL constraint defined in PostgreSQL schema.
Author: SweetIQ
Source Code: https://github.com/SweetIQ/schemats
License: MIT license
1673118960
General information
At this moment the master branch is tested with Swift 4.2 and 5.0 beta If you want to continue using EVReflection in an older version, then use the corresponding branch. Run the unit tests to see EVReflection in action.
Please update to cocoapods 1.7.0 for now you could do that using
sudo gem install cocoapods --pre
This is required for libraries that want to support other swift versions besides 5.0 For more information see the cocoapods blog
EVReflection is used in EVCloudKitDao and EVWordPressAPI
In most cases EVReflection is very easy to use. Just take a look the section It's easy to use. But if you do want to do non standard specific things, then EVReflection will offer you an extensive range of functionality.
There are extension available for using EVReflection with XMLDictionairy, Realm, CloudKit, Alamofire and Moya with RxSwift or ReactiveSwift
All these extens can be installed by adding something like this in your podfile:
pod 'EVReflection/MoyaRxSwift'
For Carthage there is not (yet) an extension for all above extension. If needed, please let me know. For carthage you can use:
github "evermeer/EVReflection"
Defining an object. You only have to set EVObject as it's base class (or extend an NSObject with EVReflectable):
class User: EVObject {
var id: Int = 0
var name: String = ""
var friends: [User]? = []
}
Parsing JSON to an object:
let json:String = "{\"id\": 24, \"name\": \"Bob Jefferson\", \"friends\": [{\"id\": 29, \"name\": \"Jen Jackson\"}]}"
let user = User(json: json)
Parsing JSON to an array of objects:
let json:String = "[{\"id\": 27, \"name\": \"Bob Jefferson\"}, {\"id\": 29, \"name\": \"Jen Jackson\"}]"
let array = [User](json: json)
Parsing from and to a dictionary:
let dict = user.toDictionary()
let newUser = User(dictionary: dict)
XCTAssert(user == newUser, "Pass")
Saving and loading an object to and from a file:
user.saveToTemp("temp.dat")
let result = User(fileNameInTemp: "temp.dat")
XCTAssert(theObject == result, "Pass")
Mapping object to another type:
let administrator: Administrator = user.mapObjectTo()
If you want to do the same but you have XML, then you can achieve that using the XML subspec 'pod EVReflection/XML' It is a simple way to parse XML. With that your code will look like this:
let xml = "<user><id>27</id><name>Bob</name><friends><user><id>20</id><name>Jen</name></user></friends></user>"
let user = User(xmlString: xml)
'EVReflection' is available through the dependency manager CocoaPods. You do have to use cocoapods version 0.36 or later
You can just add EVReflection to your workspace by adding the following 2 lines to your Podfile:
use_frameworks!
pod "EVReflection"
You can also use the Swift2.2 or Swift2.3 version of EVReflection. You can get that version by using the podfile command:
use_frameworks!
pod "EVReflection"', :git => 'https://github.com/evermeer/EVReflection.git', :branch => 'Swift2.2'
Version 0.36 of cocoapods will make a dynamic framework of all the pods that you use. Because of that it's only supported in iOS 8.0 or later. When using a framework, you also have to add an import at the top of your swift file like this:
import EVReflection
If you want support for older versions than iOS 8.0, then you can also just copy the files from the pod folder to your project. You do have to use the Swift2.3 version or older. iOS 7 support is dropped from Swift 3.
Be aware that when you have your object definitions in a framework and not in your main app, then you have to let EVReflection know that it should also look in that framework for your classes. This can easilly be done by using the following one liner (for instance in the appdelegate)
EVReflection.setBundleIdentifier(YourDataObject.self)
Clone EVReflection to your desktop to see these and more unit tests
func testEquatable() {
var theObjectA = TestObject2()
theObjectA.objectValue = "value1"
var theObjectB = TestObject2()
theObjectB.objectValue = "value1"
XCTAssert(theObjectA == theObjectB, "Pass")
theObjectB.objectValue = "value2"
XCTAssert(theObjectA != theObjectB, "Pass")
}
func testHashable() {
var theObject = TestObject2()
theObject.objectValue = "value1"
var hash1 = theObject.hash
NSLog("hash = \(hash)")
}
func testPrintable() {
var theObject = TestObject2()
theObject.objectValue = "value1"
NSLog("theObject = \(theObject)")
}
func testArrayFunctions() {
let dictionaryArray: [NSDictionary] = yourGetDictionaryArrayFunction()
let userArray = [User](dictionaryArray: dictionaryArray)
let newDictionaryArray = userArray.toDictionaryArray()
}
func testMapping() {
let player = GamePlayer()
player.name = "It's Me"
let administrator = GameAdministrator(usingValuesFrom: player)
}
Direct conversion from a NSDictionary (or an array of NSDictionaries) to json and back.
let dict1: NSDictionary = [
"requestId": "request",
"postcode": "1111AA",
"houseNumber": "1"
]
let json = dict1.toJsonString()
let dict2 = NSMutableDictionary(json: json)
print("dict:\n\(dict1)\n\njson:\n\(json)\n\ndict2:\n\(dict2)")
// You can do the same with arrays
let array:[NSDictionary] = [dict1, dict2]
let jsonArray = array.toJsonStringArray()
let array2 = [NSDictionary](jsonArray: jsonArray)
print("json array: \n\(jsonArray)\n\narray2:\n\(array2)")
This is how you can parse a .plist into an object model. See EVReflectionIssue124.swift to see it working.
if let path = Bundle(for: type(of: self)).path(forResource: "EVReflectionIssue124", ofType: "plist") {
if let data = NSDictionary(contentsOfFile: path) {
let plistObject = Wrapper(dictionary: data)
print(plistObject)
}
}
If you want to parse XML, then you can use the pod subxpec EVReflection/XML
let xml: String = "<data><item name=\"attrib\">itemData</item></data>"
let xmlObject = MyObject(xml: xml)
print(xmlObject)
It is possible to extend other objects with the EVReflectable protocol instead of changing the base class to EVObject. This will let you add the power of EVReflection to objects that also need another framework. In some cases you still need some aditional code. For a sample see the Realm and NSManagedObject subspecs. The most basic way to extend your objects is like this:
import EVReflection
extension MyObject : EVReflectable { }
With almost any EVReflection function you can specify what kind of conversion options should be used. This is done using an option set. You can use the following conversion options:
In EVReflection all functions will use a default conversion option specific to it's function. The following 4 default conversion types are used:
If you want to change one of the default conversion types, then you can do that using something like:
ConversionOptions.DefaultNSCoding = [.PropertyMapping]
If you have JSON fields that are Swift keywords, then prefix the property with an underscore. So the JSON value for self will be stored in the property \_self
. At this moment the following keywords are handled:
"self", "description", "class", "deinit", "enum", "extension", "func", "import", "init", "let", "protocol", "static", "struct", "subscript", "typealias", "var", "break", "case", "continue", "default", "do", "else", "fallthrough", "if", "in", "for", "return", "switch", "where", "while", "as", "dynamicType", "is", "new", "super", "Self", "Type", "COLUMN", "FILE", "FUNCTION", "LINE", "associativity", "didSet", "get", "infix", "inout", "left", "mutating", "none", "nonmutating", "operator", "override", "postfix", "precedence", "prefix", "right", "set", "unowned", "unowned", "safe", "unowned", "unsafe", "weak", "willSet", "private", "public"
When creating objects from JSON EVReflection will automatically detect if snake_case (keys are all lowercase and words are separated by an underscore) should be converted to PascalCase or camelCase property names. See Conversion options for when this function will be called.
When exporting object to a dictionary or JSON string you will have an option to specify that you want a conversion to snake_case or not. The default is .DefaultDeserialize which will also convert to snake case.
let jsonString = myObject.toJsonString([.DefaultSerialize])
let dict = myObject.toDictionary([PropertyConverter, PropertyMapping, SkipPropertyValue])
It's also possible to create a custom property mapping. You can define if an import should be ignored, if an export should be ignored or you can map a property name to another key name (for the dictionary and json). For this you only need to implement the propertyMapping function in the object. See Conversion options for when this function will be called.
public class TestObject5: EVObject {
var Name: String = "" // Using the default mapping
var propertyInObject: String = "" // will be written to or read from keyInJson
var ignoredProperty: String = "" // Will not be written or read to/from json
override public func propertyMapping() -> [(keyInObject: String?, keyInResource: String?)] {
return [(keyInObject: "ignoredProperty",keyInResource: nil), (keyInObject: "propertyInObject",keyInResource: "keyInJson")]
}
}
You can also use your own property converters. For this you need to implement the propertyConverters function in your object. For each property you can create a custom getter and setter that will then be used by EVReflection. In the sample below the JSON texts 'Sure' and 'Nah' will be converted to true or false for the property isGreat. See Conversion options for when this function will be called.
public class TestObject6: EVObject {
var isGreat: Bool = false
override func propertyConverters() -> [(key: String, decodeConverter: ((Any?) -> ()), encodeConverter: (() -> Any?))] {
return [
( // We want a custom converter for the field isGreat
key: "isGreat"
// isGreat will be true if the json says 'Sure'
, decodeConverter: { self.isGreat = ($0 as? String == "Sure") }
// The json will say 'Sure if isGreat is true, otherwise it will say 'Nah'
, encodeConverter: { return self.isGreat ? "Sure": "Nah"})
]
}
}
You can add generic cod to encode or decode multiple or all properties in an object. This can be used for instance for base64, unicode and encription. Here is a base64 sample:
class SimleEncodingDecodingObject : EVObject{
var firstName: String?
var lastName: String?
var street: String?
var city: String?
override func decodePropertyValue(value: Any, key: String) -> Any? {
return (value as? String)?.base64Decoded?.string ?? value
}
override func encodePropertyValue(value: Any, key: String) -> Any {
return (value as? String)?.base64Encoded.string ?? value
}
}
extension String {
var data: Data { return Data(utf8) }
var base64Encoded: Data { return data.base64EncodedData() }
var base64Decoded: Data? { return Data(base64Encoded: self) }
}
extension Data {
var string: String? { return String(data: self, encoding: .utf8) }
}
If you want to serialize an object to a dictionary or json but the structure should be different than the object itself, then instead of using propertyConverers, you can also convert the entire object by implementing the customConverter function. In the example below the entire object will be serialized to just a string. You could also return a dictionary that represents the custom structure or an array if the object should have been an array
override func customConverter() -> AnyObject? {
return "Object not serialized"
}
If you have a custom type that requires special conversion, then you can extend it with the EVCustomReflectable protocol. A good implementation for this can be found in the Realm subspec for the List type. The converter is implemented like this:
extension List : EVCustomReflectable {
public func constructWith(value: Any?) -> EVCustomReflectable {
if let array = value as? [NSDictionary] {
self.removeAll()
for dict in array {
if let element: T = EVReflection.fromDictionary(dict, anyobjectTypeString: _rlmArray.objectClassName) as? T {
self.append(element)
}
}
}
return self
}
public func toCodableValue() -> Any {
return self.enumerated().map { ($0.element as? EVReflectable)?.toDictionary() ?? NSDictionary() }
}
}
For the usage, please have a look at the Realm unittest
When there is a need to not (de)serialize specific values like nil NSNull or empty strings you can implement the skipPropertyValue function and return true if the value needs to be skipped. See Conversion options for when this function will be called.
class TestObjectSkipValues: EVObject {
var value1: String?
var value2: [String]?
var value3: NSNumber?
override func skipPropertyValue(value: Any, key: String) -> Bool {
if let value = value as? String where value.characters.count == 0 || value == "null" {
print("Ignoring empty string for key \(key)")
return true
} else if let value = value as? NSArray where value.count == 0 {
print("Ignoring empty NSArray for key\(key)")
return true
} else if value is NSNull {
print("Ignoring NSNull for key \(key)")
return true
}
return false
}
}
Before setting a value the value will always be validated using the standard validateValue KVO function. This means that for every property you can also create a validation function for that property. See the sample below where there is a validateName function for the name property.
enum MyValidationError: ErrorType {
case TypeError,
LengthError
}
public class GameUser: EVObject {
var name: String?
var memberSince: NSDate?
var objectIsNotAValue: TestObject?
func validateName(value:AutoreleasingUnsafeMutablePointer<AnyObject?>) throws {
if let theValue = value.memory as? String {
if theValue.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) < 3 {
NSLog("Validating name is not long enough \(theValue)")
throw MyValidationError.LengthError
}
NSLog("Validating name OK \(theValue)")
} else {
NSLog("Validating name is not a string: \(value.memory)")
throw MyValidationError.TypeError
}
}
}
You should be able to solve all problems with parsing your json to an object. If you get warnings and you know they don't matter and you want to stop them from printin you can suppress all print warings by calling the followin line of code:
PrintOptions.Active = .None
If you then want to turn on the print output, then just call:
PrintOptions.Active = .All
It's also possible to enable printing for specific warning types. Here is the line of code that is equal to setting it to .All. Just leave out the type that you want to suppress.
PrintOptions.Active = [.UnknownKeypath, .IncorrectKey, .ShouldExtendNSObject, .IsInvalidJson, .MissingProtocol, .MissingKey, .InvalidType, .InvalidValue, .InvalidClass, .EnumWithoutAssociatedValue]
There is also support for class level validation when deserializing to an object. There are helper functions for making keys required or not allowed. You can also add custom messages. Here is some sample code about how you can implement such a validation
public class ValidateObject: EVObject {
var requiredKey1: String?
var requiredKey2: String?
var optionalKey1: String?
override public func initValidation(dict: NSDictionary) {
self.initMayNotContainKeys(["error"], dict: dict)
self.initMustContainKeys(["requiredKey1", "requiredKey2"], dict: dict)
if dict.valueForKey("requiredKey1") as? String == dict.valueForKey("optionalKey1") as? String {
// this could also be called in your property specific validators
self.addStatusMessage(.Custom, message: "optionalKey1 should not be the same as requiredKey1")
}
}
}
You could then test this validation with code like:
func testValidation() {
// Test missing required key
let json = "{\"requiredKey1\": \"Value1\"}"
let test = ValidateObject(json: json)
XCTAssertNotEqual(test.evReflectionStatus(), .None, "We should have a not .None status")
XCTAssertEqual(test.evReflectionStatuses.count, 1, "We should have 1 validation result")
for (status, message) in test.evReflectionStatuses {
print("Validation result: Status = \(status), Message = \(message)")
}
}
You can deserialize json to an object that uses inheritance. When the properties are specified as the base class, then the correct specific object type will be returned by the function getSpecificType
. See the sample code below or the unit test in EVReflectionInheritanceTests.swift
class Quz: EVObject {
var fooArray: Array<Foo> = []
var fooBar: Foo?
var fooBaz: Foo?
}
class Foo: EVObject {
var allFoo: String = "all Foo"
// What you need to do to get the correct type for when you deserialize inherited classes
override func getSpecificType(_ dict: NSDictionary) -> EVReflectable {
if dict["justBar"] != nil {
return Bar()
} else if dict["justBaz"] != nil {
return Baz()
}
return self
}
}
class Bar : Foo {
var justBar: String = "For bar only"
}
class Baz: Foo {
var justBaz: String = "For baz only"
}
EVReflection is trying to handle all types. With some types there are limitations in Swift. So far there is a workaround for any of these limitations. Here is an overview:
For all these issues there are workarounds. The easiest workaround is just using a difrent type like:
If you want to keep on using the same type, You can override the setValue forUndefinedKey in the object itself. See WorkaroundsTests.swift and WorkaroundSwiftGenericsTests.swift to see the workaround for all these types in action.
For generic properties the protocol EVGenericsKVC is required. see WorkaroundSwiftGenericsTests.swift
For arrays with nullable objects or Set's like [MyObj?] or Set the protocol EVArrayConvertable is required. see WorkaroundsTests.swift
For Swift Dictionaries (and not NSDictionary) the protocol EVDictionaryConvertable is required. See WorkaroundsTests.swift
EVReflection is available under the MIT 3 license. See the LICENSE file for more info.
Also see my other public source iOS libraries:
Author: Evermeer
Source Code: https://github.com/evermeer/EVReflection
License: View license
1673091086
Runtime is a Swift library to give you more runtime abilities, including getting type metadata, setting properties via reflection, and type construction for native swift objects.
TypeInfo
exposes metadata about native Swift structs, protocols, classes, tuples and enums. It captures the properties, generic types, inheritance levels, and more.
Lets say you have a User struct:
struct User {
let id: Int
let username: String
let email: String
}
To get the TypeInfo
of User
, all that you have to do is:
let info = try typeInfo(of: User.self)
Within the TypeInfo
object, it contains a list of PropertyInfo
which represents all properties for the type. PropertyInfo
exposes the name and type of the property. It also allows the getting and setting of a value on an object.
Using the same User
object as before first we get the TypeInfo
and the property we want.
let info = try typeInfo(of: User.self)
let property = try info.property(named: "username")
To get a value:
let username = try property.get(from: user)
To set a value:
try property.set(value: "newUsername", on: &user)
It's that easy! 🎉
Runtime also supports building an object from it's Type
. Both structs and classes are supported.
To build a User
object:
let user = try createInstance(type: User.self)
FunctionInfo
exposes metadata about functions. Including number of arguments, argument types, return types, and whether it can throw an error.
func doSomething(a: Int, b: Bool) throws -> String {
return ""
}
let info = functionInfo(of: doSomething)
Q: When getting and setting a value does it work typeless? (i.e. object casted as Any
)
A: Yes! The whole library was designed with working typeless in mind.
Q: When creating a new instance of a class is it still protected by ARC?
A: Yes! The retain counts are set properly so ARC can do its job.
Runtime is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'Runtime'
You can install Runtime via Swift Package Manager by adding the following line to your Package.swift
:
import PackageDescription
let package = Package(
[...]
dependencies: [
.Package(url: "https://github.com/wickwirew/Runtime.git", majorVersion: XYZ)
]
)
Contributions are welcome and encouraged!
Want to know how it works? Here's an article on how it was implemented.
Want to learn about Swift memory layout? Mike Ash gave and awesome talk on just that.
Author: Wickwirew
Source Code: https://github.com/wickwirew/Runtime
License: MIT license
1672983975
npm install reflect-metadata
class C {
@Reflect.metadata(metadataKey, metadataValue)
method() {
}
}
Reflect.defineMetadata(metadataKey, metadataValue, C.prototype, "method");
let obj = new C();
let metadataValue = Reflect.getMetadata(metadataKey, obj, "method");
// define metadata on an object or property
Reflect.defineMetadata(metadataKey, metadataValue, target);
Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey);
// check for presence of a metadata key on the prototype chain of an object or property
let result = Reflect.hasMetadata(metadataKey, target);
let result = Reflect.hasMetadata(metadataKey, target, propertyKey);
// check for presence of an own metadata key of an object or property
let result = Reflect.hasOwnMetadata(metadataKey, target);
let result = Reflect.hasOwnMetadata(metadataKey, target, propertyKey);
// get metadata value of a metadata key on the prototype chain of an object or property
let result = Reflect.getMetadata(metadataKey, target);
let result = Reflect.getMetadata(metadataKey, target, propertyKey);
// get metadata value of an own metadata key of an object or property
let result = Reflect.getOwnMetadata(metadataKey, target);
let result = Reflect.getOwnMetadata(metadataKey, target, propertyKey);
// get all metadata keys on the prototype chain of an object or property
let result = Reflect.getMetadataKeys(target);
let result = Reflect.getMetadataKeys(target, propertyKey);
// get all own metadata keys of an object or property
let result = Reflect.getOwnMetadataKeys(target);
let result = Reflect.getOwnMetadataKeys(target, propertyKey);
// delete metadata from an object or property
let result = Reflect.deleteMetadata(metadataKey, target);
let result = Reflect.deleteMetadata(metadataKey, target, propertyKey);
// apply metadata via a decorator to a constructor
@Reflect.metadata(metadataKey, metadataValue)
class C {
// apply metadata via a decorator to a method (property)
@Reflect.metadata(metadataKey, metadataValue)
method() {
}
}
function ParamTypes(...types) {
return (target, propertyKey) => {
const symParamTypes = Symbol.for("design:paramtypes");
if (propertyKey === undefined) {
target[symParamTypes] = types;
}
else {
const symProperties = Symbol.for("design:properties");
let properties, property;
if (Object.prototype.hasOwnProperty.call(target, symProperties)) {
properties = target[symProperties];
}
else {
properties = target[symProperties] = {};
}
if (Object.prototype.hasOwnProperty.call(properties, propertyKey)) {
property = properties[propertyKey];
}
else {
property = properties[propertyKey] = {};
}
property[symParamTypes] = types;
}
};
}
function ParamTypes(...types) {
// as propertyKey is effectively optional, its easier to use here
return (target, propertyKey) => { Reflect.defineMetadata("design:paramtypes", types, target, propertyKey); }
// vs. having multiple overloads with the target and key in the front:
//
// return (target, propertyKey) => {
// if (propertyKey === undefined) {
// Reflect.defineMetadata(target, "design:paramtypes", types);
// }
// else {
// Reflect.defineMetadata(target, propertyKey, "design:paramtypes", types);
// }
// }
//
// vs. having a different methods for the class or a property:
//
// return (target, propertyKey) => {
// if (propertyKey === undefined) {
// Reflect.defineMetadata(target, "design:paramtypes", types);
// }
// else {
// Reflect.definePropertyMetadata(target, propertyKey, "design:paramtypes", types);
// }
// }
}
"experimentalDecorators": true
to your tsconfig.json file."emitDecoratorMetadata": true
to your tsconfig.json file.Author: rbuckton
Source Code: https://github.com/rbuckton/reflect-metadata
License: Apache-2.0 license
1669013905
Learn how what and how reflection works in Kotlin with this tutorial
In programming, reflection is a programming language’s ability to inspect and interact with statically defined classes, functions, and properties during runtime.
The feature is particularly useful when you receive an object instance of an unknown class.
By using reflection, you can check if a particular object has a certain method, and call that method when it exists.
To use reflection in Kotlin, you need to include the kotlin-reflect
library in your project:
dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect:1.6.10")
}
The library contains the runtime component required for using Kotlin reflection features.
Next, let’s see how you can get class, function, and property references using Kotlin reflection feature.
Suppose you have a Dog
class with the following definitions:
class Dog(var name: String) {
fun bark() {
println("Bark!")
}
fun bark(sound: String) {
println(sound)
}
private fun hello() {
println("Hello! My name is $name")
}
}
To get the class reference in Kotlin, you can use the class literal syntax ::class
as shown below:
val classRef = Dog::class
Alternatively, you can get the class reference from an object instance by using the same ::class
syntax on the instance:
val myDog = Dog("Puppy")
val classRef = myDog::class
Getting the class reference from an object is also known as a bounded class reference.
Once you have the class reference, you can access the properties of the reference to find out more about that class.
For example, you can find the name of the class and check if that class is a data
class:
println(classRef.simpleName) // Dog
println(classRef.qualifiedName) // org.metapx.Dog
println(classRef.isData) // false
In Kotlin, the class reference is identified as the Kclass
type which stands for Kotlin class.
You can check the Kclass
documentation for all properties and methods you can access to find out about the class from its reference.
Aside from inspecting the class, the Kclass
also has some interesting abilities. The createInstance()
method, for example, allows you to create a new object from the class reference:
val secondDog = classRef.createInstance()
But keep in mind that the createInstance()
method only works when the class has a constructor with optional or no parameter.
An error will be thrown when no constructor fulfills the criteria.
You can also get access to the methods of the class reference regardless of their access modifier.
This means even private
functions of a class can be accessed from its reference.
The memberFunctions
property of Kclass
stores all methods of the class as a Collection
:
val myDog = Dog("Puppy")
val classRef = myDog::class
classRef.memberFunctions.forEach {
println(it.name)
}
The output will be as follows:
bark
bark
hello
equals
hashCode
toString
Next, you can call the class function from its reference as follows:
val myDog = Dog("Puppy")
val classRef = myDog::class
val barkRef = classRef.memberFunctions.find {
it.name == "bark"
}
barkRef?.call(myDog)
First, you need to use the find()
function to retrieve the function reference.
Then, check if the function reference is found using the null-safe call.
When the reference is found, use the call()
method from the function reference Kfunction
type.
The first argument of the call()
method must be an instance of the class reference, which is why myDog
object is passed into the method.
When your function is private
, you need to change the isAccessible
property of the function reference as true
first before calling the function:
val helloRef = classRef.memberFunctions.find {
it.name == "hello"
}
helloRef?.isAccessible = true
helloRef?.call(myDog)
And that’s how you access the methods of a class using its reference.
The properties of a Kotlin class reference can be accessed the same way you access its methods.
The properties of a class are stored in memberProperties
as a Collection
.
For example, you can get the name
property value of the myDog
instance as follows:
val myDog = Dog("Puppy")
val classRef = myDog::class
val nameRef = classRef.memberProperties.find {
it.name == "name"
}
println(nameRef?.getter?.call(myDog)) // Puppy
A property reference is an instance of KProperty
type. The value of the property is retrieved by calling the getter()
method.
To change the value of the name
property, you need to cast the property into KMutableProperty
first as shown below:
val myDog = Dog("Puppy")
val classRef = myDog::class
val nameRef = classRef.memberProperties.find {
it.name == "name"
} as KMutableProperty<*>?
nameRef?.setter?.call(myDog, "Jacob")
println(myDog.name) // Jacob
The KMutableProperty
holds the setter()
method, which you need to call to set the value of the property.
Now you’ve learned how to access methods and properties from a class reference.
Next, let’s look at how you can get a function reference with Kotlin reflection
You can get a reference to a named Kotlin function by using the ::
operator.
Here’s an example:
fun hello() {
println("Hello World!")
}
val funRef = ::hello
funRef() // Hello World!
The funRef
above will be an instance of Kfunction
type, which represents a function with introspection capabilities.
Now you’ve learned what the Kotlin reflection feature is and how it works with some examples. Reflection is a powerful feature that’s only used for specific requirements.
Because of its ability to find inspect a source code, it’s frequently used when developing a framework or library for further development.
JUnit and Spring frameworks are notable for using reflection in their source code.
The library author won’t know the classes and functions created by the user. Reflection allows the framework to deal with classes and functions without knowing about them in advance.
Original article source at: https://sebhastian.com/
1667392749
A lens is a transparent material that concentrates or disperses light rays when it passes through them by refraction. According to the shape and purpose of the lens, they are classified into two types convex lens and concave lens.
https://www.pw.live/physics-articles/convex-lens
#convexlens #lens #mirror #physics #reflection #physicsarticles
1647114840
In this video we will do a primer of reflection in Swift. Using the Mirror API we will learn about various use cases of this simple but powerful api. We will work in Xcode 13 and Swift 5.
💻 Source Code: https://patreon.com/iOSAcademy
1637217540
Image Reflection using CSS Property -webkit-box-reflect | Webkit Coding
Hello friend today in this tutorial you will learn about how you can create a image / object #reflection using CSS_property -webkit-box-reflect. I hope you will like this tutorial as our all other tutorials don't forget to subscribe and press the bell icon for never miss any new updates
Click on Time-Stamp to jump on
00:06 - Preview
00:32 - Totorial
1598465520
We know that a function or method will:
But there is a third thing a function can return and that is NotImplemented. The simple meaning of return NotImplemented by a function is that the function is declaring that it cannot find the result but instead of raising the exception, it will transfer the control to another function, hoping that the other function will get the result. The other function is known as Reflection Function.
We will explore the whole idea in context to Object Oriented Programming and associated Magic Methods (also known as special methods or dunder methods) which are used for different special functionalities and we will just see the ones used to define the support for different operator (e.g. +,-,* etc.) for objects of one class.
Let’s see this simple Point class defined for the points on XY plane:
class Point:
def __init__(self,x,y):
self.x=x
self.y=y
def __str__(self):
return f'({self.x},{self.y})'
p1=Point(3,4)
print(p1)
The __str__ method is defined for the proper representation of the point object and the last print statement will print the point as (3,4).
If we want to define a function for the addition of two point objects, it can be done as:
def addPoints(self,other):
return Point(self.x+other.x,self.y+other.y)
And then we can use it as:
p1=Point(3,4)
p2=Point(1,1)
p3=p1.addPoints(p2)
print(p3)
This will display (4,5) as output.
But it will be better if instead of using a custom named function like addPoints, we could directly apply + operator like we do for simple numbers and a few other data types e.g. 4+5. At present if we use + operator on two point objects as:
p1=Point(3,4)
p2=Point(1,1)
p3=p1+p2
It will generate following error which is self-explanatory:
TypeError: unsupported operand type(s) for +: ‘Point’ and ‘Point’
If we change the name of the function addPoints to __add__ which is a magic method, the + operator applied between two point objects will call this method.
See the complete code here:
class Point:
def __init__(self,x,y):
self.x=x
self.y=y
def __str__(self):
return f'({self.x},{self.y})'
def __add__(self,other):
return Point(self.x+other.x,self.y+other.y)
p1=Point(3,4)
p2=Point(1,1)
p3=p1+p2
print(p3)
And the output will be (4,5).
We have special methods for other operators too e.g. __sub__ is for subtraction ( - ), __mul__ is for multiplication ( * ), __div__ is for division( / ) and a few more.
In fact, when we apply + operator between any data types, interpreter actually executes __add__ method on those e.g. for 3+4, interpreter will execute 3.__add__(4) and the method __add__ is defined in int class for addition of two integers. Similarly, if a and b are two lists, a+b will result into a.__add__(b) and __add__ is defined inside list class as concatenation of the lists.
Now let’s define __mul__ for the Point class but not for the multiplication of two point objects but to multiply the Point object with a number, resulting into a new point with scaled x and y components. It will be done by adding __mul__ method as shown here:
class Point:
def __init__(self,x,y):
self.x=x
self.y=y
def __str__(self):
return f'({self.x},{self.y})'
def __add__(self,other):
return Point(self.x+other.x,self.y+other.y)
def __mul__(self,num):
return Point(self.x*num,self.y*num)
p1=Point(3,4)
p2=p1*2
print(p2)
The output will be (6,8).
But instead of p1*2 if we execute 2*p1, then instead of getting the same result, we get an error as:
TypeError: unsupported operand type(s) for *: ‘int’ and ‘Point’
This is something we will resolve using the concept of NotImplemented and Reflection Function.
When a function returns NotImplemented, interpreter will run the reflection function associated with that function after flipping the input arguments. For example, if original function has input arguments as a and b, at returning NotImplemented, interpreter will run the associated reflection function on b and a.
Second important thing is that the reflection functions associated with different functions are predefined in Python and you cannot make some function as reflection of some other function by your own. The reflection function of __add__ is __radd__, reflection function of __mul__ is __rmul__ and so on for different magic methods.
So, lets see what happened when interpreter executed 2*p1 in above program.
As described earlier that 2*p1 will result into 2.__mul__(p1), so interpreter will apply __mul__ on 2 which is an integer. Therefore, interpreter will search for __mul__ method inside int class. Interpreter will find the method in int class but that method defines multiplication between two integers and may be between an integer and some other data type but not between an integer and the Point class object. And in such cases (unsupported datatype), the __mul__ of int class returns NotImplemented.
With NotImplemented returned, interpreter will run the reflection method i.e. __rmul__ on flipped input arguments as p1.__rmul__(2). And you can see that the method is applied on p1 which is Point class object and hence interpreter will search for this method (__rmul__) in Point class and will fail since this method is not defined inside Point class. So it will return back to the original function (of int class) and will generate this error:
TypeError: unsupported operand type(s) for *: ‘int’ and ‘Point’
The whole process is given here step by step for further clarification:
So if you have followed along these steps, you probably have figured out the solution which is that we must define __rmul__ in Point class, so that p1.__rmul__(2) should get executed.
What should be inside this function?
p1.__rmul__(2) means that self is p1 and second input argument is 2 and we need to return a new point with scaled xy coordinates. So, we should have __rmul__ defined as:
def __rmul__(self,num):
return Point(self.x*num,self.y*num)
But this is exactly what __mul__ does, and hence we can also do as:
def __rmul__(self,num):
return self*num
Above return statement with multiplication will call the __mul__ method.
Even a better approach will be to just declare:
__rmul__=__mul__
Because the function are also Objects and we are saying that __rmul__ is referring to same object as __mul__.
The complete code will be:
class Point:
def __init__(self,x,y):
self.x=x
self.y=y
def __str__(self):
return f'({self.x},{self.y})'
def __add__(self,other):
return Point(self.x+other.x,self.y+other.y)
def __mul__(self,num):
return Point(self.x*num,self.y*num)
__rmul__=__mul__
p1=Point(3,4)
p2=2*p1
print(p2)
And now we will get the correct output as (6,8).
You can see that it is just the one last line added in Point class i.e. __rmul__=__mul__ and it resolved a big problem. And that is all because of the power of NotImplemented and Reflection Function support of Python.
Please don’t forget to subscribe: Learning Orbis
You can find further detail with more practice examples in this video:
If you need more detail on Magic Methods, you can find that in the following videos:
#notimplemented #reflection #python #programming #oop
1596968220
In our previous article, we discussed how we could read the values of private fields from a different class in Java. However, there can be scenarios when we need to set the values of fields, such as in some libraries where we don’t have access to the fields.
In this quick tutorial, we’ll discuss how can we set the values of fields from a different class in Java by using the Reflection API.
Note that we’ll be using the same Person class for the examples here as we used in our previous article.
We can** set the fields that are primitives by using the Field#setXxx methods**.
#java #reflection
1596142740
In this quick tutorial, we’re going to take a look at the difference between Class.isInstance and Class.isAssignableFrom. We’ll learn how to use each method and what the differences are between them.
Let’s set up an interface and a couple of classes to use while we explore the Class.isInstance and Class.isAssignableFrom functionality.
First, let’s define an interface:
public interface Shape {}
Next, let’s define a class that implements Shape:
public class Triangle implements Shape {}
Now, we’ll create a class that extends Triangle:
public class IsoscelesTriangle extends Triangle {}
The isInstance method on the Class class is equivalent to the instanceof operator. The isInstance method was introduced in Java 1.1 because it can be used dynamically. This method will return true if the argument isn’t null and can be successfully cast to the reference type without raising a ClassCastException.
#java #reflection
1591862858
We have this big project we’re working on (which I have written about before) and one of the things we need to do on this project is automatic logging of changes made to model objects. I’ve worked out a way to do this generically, for any object, and I think
#asp.net #reflection in .net #reflection #.net #programming