How to get all property of a class - crystal-lang

I wish get all the properties of a class.
When I pretty print an object, I have all the information of the object, so I suppose there is a way to get the properties of a class directly.
For example :
class Location
property latitude : Float64
property longitude : Float64
end
How do you determine that Location has latitude and longitude as properties.
Thanks

I also found this way
class Location
property latitude : Float64
property longitude : Float64
def initialize(#latitude, #longitude)
end
# Call this on class instance
def methods
{{ pp! #type.instance_vars }}
end
end
loc = Location.new(-13.8948894, 33.775556)
pp loc.methods #=> #type.instance_vars # => [latitude, longitude]

Crystal has no built-in option for retrieve methods but you can define it:
class Location
property latitude : Float64
property longitude : Float64
def initialize(#latitude, #longitude)
end
# Call this on class instance
def methods
{{ #type.methods.map(&.name).select { |m| !m.includes?("=") }.map(&.stringify) }}
end
end
loc = Location.new(-13.8948894, 33.775556)
pp loc.methods #=> ["latitude", "longitude", "initialize", "methods"]
property is a macro that will be unwrapped as getter/setter method in compilation and as result you can find it in the list of methods.

Related

LiteDB - find data object via List.contains

A simplified version. I have two classes:
Public Class mSystem
Public Property ID as ObjectID
Public Property Name as string
End Class
Public Class mEmulator
Public Property ID as ObjectID
Public Property Name as string
<BsonRef("mSystems")>
Public Property AssociatedSystems as New List(Of mSystem)
End Class
Public Class Main
Public Sub EmaultorsLinkedToSystem
dim SelectedSystem as mSystem = db.Collections.mSystems.Find(Function(x) x.Name = "Sony Playstation").FirstOrDefault
test = db.Collections.mEmulators.Include(Function(x) x.AssociatedSystems).Find(Function(y) y.AssociatedSystems.Contains(SelectedSystem)).ToList
End sub
End Class
Now I know one mEmulator data object has "Sony Playstation" in its List(of mSystem). However, test returns null. Why isn't this finding it? I've tried a few permutations, but cant get this to work. Any ideas?
The Include method is used for resolving references to other collections, and you're not using BsonRef with AssociatedSystems (at least not in this example you provided). In your example, the instances of mSystem in AssociatedSystems are not being stored in a separate collection, but as an array of embedded documents in the emulators collection.
Try removing the Include call, it should work fine.

Type restriction for class members in case/when

Let's say I have following class Message representing a message in a chat.
class Message
JSON.mapping({
message_id: {type: Int32},
text: {type: String, nilable: true},
photo: {type: Photo, nilable: true},
sticker: {type: Sticker, nilable: true},
})
MESSAGE_TYPES = %w( text photo sticker )
{% for type in MESSAGE_TYPES %}
def {{ type.id }}?
! {{ type.id }}.nil?
end
{% end %}
end
Every message can be either text, or photo, or sticker. But only one of those. For instance, if message is a text, then photo and sticker properties are nil, but text is not nil. If message is a sticker, only sticker is not nil. And so on. Real class has much more direct types and inferred types.
Upon receiving this message, I want to process it in a way specific for particular type. Something like:
case message
when .text?
p message.text
when .photo?
p message.photo.file_id
when .sticker?
p message.sticker.file_id
end
Of course, it won't work, because in each when clause corresponding properties are nilable and undefined method 'file_id' for Nil.
Is there any way to restrict variable type in each case making sure that is message.sticker? then message.sticker is not nil and file_id exists?
Maybe I'm completely wrong in very approaching the problem and there better and cleaner ways to code this?
require "json"
class Photo
JSON.mapping(url: String)
end
struct Message
JSON.mapping(id: Int32, message: String|Photo)
end
p Message.from_json(%({"id": 5, "message": {"url": "http://example.org/"}}))
You could put all payload objects in one instance variable with a union type as Oleh Prypin suggested.
Another option, if you want to keep the current data layout, you could have the methods return the respective Type or nil and assign its value to a variable:
if text = message.text?
p text
if photo = message.photo?
p photo.file_id
if sticker = message.sticker?
p sticker.file_id
end

Accessing properties in templates by their name

given the following pieces of code:
groovy:
binding = [key1: "val1"]
def f = new File('test.template')
engine = new GStringTemplateEngine()
template = engine.createTemplate(f).make(binding)
println template.toString()
test.template:
<% keyName = "key1" %>
Is there a way to access val1 by keyName in test.template?
This:
${ binding[keyName] }
does not work (No such property: key1 for class: groovy.lang.Binding). Any ideas? Maybe the name of the map holding the properties is different?
I know I could just write:
${ key1 }
but I need to access property key1 using variable keyName.
Not sure if this is better but I got the following to work (somewhat)
Map binding = [ keyName: 'key1', key1: "val1", m: [key1:'val100', key2:'val2']]
def f = new File('test.template')
def engine = new groovy.text.GStringTemplateEngine()
def template = engine.createTemplate(f).make(binding)
println template.toString()
with the following template:
$keyName
$key1
<%= m[keyName] %>
But this relies on a submap that holds the values you are looking for.
I can see scenarios where in the binding, you pass a list of fields you want to process or display (rather than knowing them ahead of time), so you would have to get the field names from a well-known variable and then process the others possibly thru a submap.

How to get store name of Ember Data model

How can I determine the "store name" (not sure what the proper terminology is) for a given ED Model? Say I have App.Payment, is there a store method that let's me look up its corresponding name, i.e. payment (for example to use in find queries)?
For Ember Data 1.0 (and later)
modelName is a dasherized string. It stored as a class property, so if you have an instance of a model:
var model = SuperUser.create();
console.log(model.constructor.modelName); // 'super-user'
For Ember Data Pre 1.0
typeKey is the string name of the model. It gets stored as a class property of the model, so if you have an instance of a model:
var model = App.Name.create({});
console.log(model.constructor.typeKey); // 'name'
You might be looking for Ember's string dasherize method:
var fullClassName = "App.SomeKindOfPayment";
var className = fullClassName.replace(/.*\./, ""); // => "SomeKindOfPayment"
var dasherizedName = Ember.String.dasherize(className); // "some-kind-of-payment"
There might be a built-in way to do this in Ember, but I haven't found it after spending some time looking.
EDIT: Ember Data might also let you get away with passing "App.SomeKindOfPayment" when a model name is needed - it usually checks the format of the model name and updates it to the required format by itself.
store.find, store.createRecord, and other persistence methods, use the store.modelFor('myModel'). After some setup it call container.lookupFactory('model:' + key); where key is the 'myModel'. So any valid factory lookup syntax is applicable. For example:
Given a model called OrderItems you can use: order.items, order_items, order-items, orderItems.
It turns out there was no need to do this after all, and here's why:
I was trying to the the string representation of the model ("payment" for App.Payment) in order to call store.findAll("payment"). However, looking at the ED source for store, the findQuery function calls modelFor to look up the factory (App.Payment) from the string (payment), unless a factory is already provided. And the factory is easily accessible from the controller by calling this.get('model').type. There's no need to convert it to a string (and back).
Here's the relevant code from the Ember Data source.
modelFor: function(key) {
var factory;
if (typeof key === 'string') {
factory = this.container.lookupFactory('model:' + key);
Ember.assert("No model was found for '" + key + "'", factory);
factory.typeKey = key;
} else {
// A factory already supplied.
factory = key;
}
factory.store = this;
return factory;
},

How to architect DAL for WebService exposure?

We have a highly specialized DAL which sits over our DB. Our apps need to use this DAL to correctly operate against this DB.
The generated DAL (which sits on some custom base classes) has various 'Rec' classes (Table1Rec, Table2Rec) each of which represents the record structure of a given table.
Here is a sample Pseudo-class...
Public Class SomeTableRec
Private mField1 As String
Private mField1isNull As Boolean
Private mField2 As Integer
Private mField2isNull As Boolean
Public Sub New()
mField1isNull = True
mField2isNull = True
End Sub
Public Property Field1() As String
Get
Return mField1
End Get
Set(ByVal value As String)
mField1 = value
mField1isNull = False
End Set
End Property
Public ReadOnly Property Field1isNull() As Boolean
Get
Return mField1isNull
End Get
End Property
Public Property Field2() As Integer
Get
Return mField2
End Get
Set(ByVal value As Integer)
mField2 = value
mField2isNull = False
End Set
End Property
Public ReadOnly Property Field2isNull() As Boolean
Get
Return mField2isNull
End Get
End Property
End Class
Each class has properties for each of the fields...
Thus I can write...
Dim Rec as New Table1Rec
Table1Rec.Field1 = "SomeString"
Table2Rec.Field2 = 500
Where a field can accept a NULL value, there is an additional property which indicates if the value is currently null.
Thus....
Dim Rec as New Table1Rec
Table1Rec.Field1 = "SomeString"
If Table1Rec.Field1Null then
' This clearly is not true
End If
If Table1Rec.Field2Null then
' This will be true
End If
This works because the constructor of the class sets all NULLproperties to True and the setting of any FieldProperty will cause the equivalent NullProperty to be set to false.
I have recently had the need to expose my DAL over the web through a web service (which I of course intend to secure) and have discovered that while the structure of the 'Rec' class remains intact over the web... All logic is lost..
If someone were to run the previous piece of code remotely they would notice that neither condition would prove true as there is no client side code which sets null to true.
I get the feeling I have architected this all wrong, but cannot see how I should improve it.
What is the correct way to architect this?
Not sure if I fully understand the question, but you can have nullable data types in XML.
So this...
Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols
<WebService(Namespace:="http://tempuri.org/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Public Class Testing
Inherits System.Web.Services.WebService
<WebMethod()> _
Public Function GetObjects() As Generic.List(Of TestObject)
Dim list As New Generic.List(Of TestObject)
list.Add(New TestObject(Nothing, "Empty ID Object"))
list.Add(New TestObject(1, "Full ID Object"))
list.Add(New TestObject(2, Nothing))
Return list
End Function
Public Class TestObject
Public Sub New()
_name = String.Empty
_id = Nothing
End Sub
Public Sub New(ByVal id As Nullable(Of Integer), ByVal name As String)
_name = name
_id = id
End Sub
Private _name As String
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Private _id As Nullable(Of Integer)
Public Property ID() As Nullable(Of Integer)
Get
Return _id
End Get
Set(ByVal value As Nullable(Of Integer))
_id = value
End Set
End Property
End Class
End Class
outputs this (with nullable areas)
<?xml version="1.0" encoding="utf-8" ?>
<ArrayOfTestObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/">
<TestObject>
<Name>Empty ID Object</Name>
<ID xsi:nil="true" />
</TestObject>
<TestObject>
<Name>Full ID Object</Name>
<ID>1</ID>
</TestObject>
<TestObject>
<ID>2</ID>
</TestObject>
</ArrayOfTestObject>
Web services are designed to expose operation(methods) & data contracts but not internal implementation logic. This is a "good thing" in the world of service-oriented architecture. The scenario you describe is a remote/distributed object architecture. Web services will not support what you are trying to do. Please see this post for more information.