In order to do .to_json on this Struct of Book, the include JSON::Serializable is needed, there are answers for this in other languages, but I thought there should be something about this in Crystal or Crystal lang.
struct Book
include JSON::Serializable # Needed to parse JSON
def initialize(
#Id : Int32,
#Title : String,
#Author : String,
#Desc : String
)
end
end
https://crystal-lang.org/api/1.4.1/JSON.html#generating-with-to-json Here is some documentation on this.
The JSON::Serializable module automatically generates methods for JSON serialization when included.
Including JSON::Serializable will create #to_json and self.from_json methods on the current class, and a constructor which takes a JSON::PullParser. By default, these methods serialize into a json object containing the value of every instance variable, the keys being the instance variable name.
Related
I asked a more limited version of this question at In Crystal, what's the difference between inheritance and inclusion?, but I think I (and other readers) would benefit from a more comprehensive answer.
What are the differences between inheritance (with <), inclusion (with include), and extension (with extend) in Crystal classes?
Answer
Class inheritance
Inheritance with < duplicates instance attributes, instance methods, class attributes, and class methods from the inherited class into the current class.
Modules cannot inherit or be inherited.
class A
# Equivalent to `##foo = 1` with corresponding getter and setter class methods
##foo = 1
# Equivalent to `#foo = ` with corresponding getter and setter instance methods
#foo = 2
end
class B < A
end
pp A.foo == B.foo # true
pp A.new.foo == B.new.foo # true
The value of a class attribute in the superclass is not the same as the value in the subclass, but the types will be the same.
class C
class_property foo = 1
end
class D < C
end
D.foo = 9
pp C.foo == D.foo # false
A class can inherit from exactly one class.
Module inclusion
include duplicates instance attributes and instance methods from the included module into the current class or module.
Classes cannot be included.
It is possible to define class attributes and class methods on modules, but these are not duplicated by inclusion; they are ignored.
module BirdHolder
# Ignored by `include`
class_property bird = "123"
property bird = "456"
end
class Thing
include BirdHolder
end
pp Thing.new.bird # "456"
pp Thing.bird # Error: undefined method 'bird' for Thing1.class
A type (module or class) can include any number of modules.
Module extension
extend duplicates instance methods from the included module into the current class or module, but as class methods.
Classes cannot be extended.
Class attributes and class methods from the extended module are ignored.
module Talkative
# Ignored by `extend`
##stuff = "zxcv"
# Ignored by `extend`
def self.say_stuff
puts "stuff"
end
def say_stuff
puts ##stuff
end
end
class Thing
extend Talkative
##stuff = "asdf"
end
Thing.say_stuff # "asdf"
Note that ##stuff in the definition of the Talkative module refers to the class attribute of the extending class, not to the class attribute of Talkative itself.
It is not possible to extend a module that defines instance methods. This results in a compilation error.
A type (module or class) can extend exactly one module.
References
This information is based on:
Crystal Reference: Classes and Methods
Crystal Reference: Modules
Answer to my previous question by Johannes Müller
Assistance from users in the crystal-lang/crystal Gitter chatroom.
This answer is valid as of Crystal 1.0.0
I'm using PyCharm as editor. For example, I have next function:
def get_instance():
# method without doc sctring in some module
# returns instance of MyClass
return some_var
And I have second file which calls get_instance():
var = get_instance()
How I can to define type of data for value? I want to use autocomplete for variable.
For example in php it will be like this:
/** #var MyClass $var */
$var = getInstance();
After this I will be see all methods and properties of $var. How to declare docstring for python variable? I know about :type and :rtype, but if I need declare type for specific variable? Can I declare a few types? What I should do?
Note: I can't do any changes with get_instance().
I don't think you can do this via docstring, but there is a mechanism called annotation. You can append whatever expression you want to parameters following a colon : and to the function declaration itself using an arrow ->. For example:
def getInstance(param: str) -> MyClass
return some_var
I have added an input parameter for purposes of illustration here. In this case str and MyClass are objects that represent the expected input and return types, respectively. They are never used in your code directly, just stashed away in an func_annotations attribute for end users that care about that sort of things (like editors).
You can do it in the same way as description properties of class. Here example:
from example_module import MyClass
var = get_instance()
"""
:type var: MyClass
"""
Also you can use description without import like this:
var = get_instance()
"""
:type var: example_module.MyClass
"""
The second way is using PEP 484:
test = my() # type: dict
Examples of autocomplete in Pycharm:
This is my first time to see the following codes.
dataset = type('dummy', (), {})()
And I print the dataset in the console it tells me that
<__main__.dummy at Ox7feec5195e90>
Can anyone help me to figure what these codes mean?
type here is metaclass in python. The meaning of metaclass is that they create classes for us.
And the code above means that we create a class named dummy(the first parameter). The class does not inherit from any other classes, so the second parameter is ().The class does not have any attributes and methods, so the third parameter is {}.
If we want to create a class named pp including attribute a and method m and let the class inherited from class f, we can code like this:
class f():
def __init__(self):
pass
def m():
print (123)
metaclass = type("pp", (f,), {"a":33, "m":m})()
I have a Rails 4.2 application with mongoid in which I'm importing csv files with test results. I can't define all fields in the model because they change from test to test and theres always around 700 of them. I use Dynamic Attributes and importing and displaying works fine.
I'm trying to use attribute_names method to get all attribute names but all I get is those defined in the model. If I don't define anything in the model it comes back with "_id" only. attributes method on the other hand can see attributes in the actual document on the other hand.
>> #results.first.attributes.count
=> 763
>> #results.first.attribute_names
=> ["_id"]
I also tried fields.keys, same problem
>> #results.first.fields.keys
=> ["_id"]
My model at the moment looks like this
class Result
include Mongoid::Document
include Mongoid::Attributes::Dynamic
def self.import(file)
CSV.foreach(file.path, headers: true) do |row|
Result.create! row.to_hash
end
end
end
Can somebody explain how to make it work?
Any help greatly appreciated.
This part is not very clear in the documentation.
and this answer doesn't address how you can make your case works ( I really don't know)... but it has one monkey patch at the end...
all I know is why this case not working...
as the documentation states
When dealing with dynamic attributes the following rules apply:
If the attribute exists in the document, Mongoid will provide you with your standard getter and setter methods.
For example, consider a person who has an attribute of "gender" set on the document:
# Set the person's gender to male.
person[:gender] = "Male"
person.gender = "Male"
# Get the person's gender.
person.gender
this is not your case... cause as it appears you are not defining any attributes in your model...
what applies in your case (from the code you showed and problem you described)
If the attribute does not already exist on the document,
Mongoid will not provide you with the getters and setters and will enforce normal method_missing behavior.
In this case you must use the other provided accessor methods: ([] and []=) or (read_attribute and write_attribute).
# Raise a NoMethodError if value isn't set.
person.gender
person.gender = "Male"
# Retrieve a dynamic field safely.
person[:gender]
person.read_attribute(:gender)
# Write a dynamic field safely.
person[:gender] = "Male"
person.write_attribute(:gender, "Male")
as you can see... there is no way for mongoid to add the setter and getter methods in runtime...
Monkey Patch
you could add a field (maybe string, array, hash, whatever suites you) to the document (attribute exists in the document)
on populating the document from the CSV row.. just save what are the fields of the CSV in that field... (hold the CSV keys in it)
use your predefined field (that holds the keys) instead of using .keys.
code example in your case.
class Result
include Mongoid::Document
include Mongoid::Attributes::Dynamic
field :the_field_that_holds_the_keys, type: Array
# ...
end
and in your controller:
#results.first.some_attribute
#=> method missing error
#results.first[:some_attribute]
#=> some_value
#results.first.the_field_that_holds_the_keys
#=> [:some_attribute, :some_other_attribute, :yada]
Let's say I have a class like this
class MyClass(AnotherClass):
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
def f(self):
#dostuff
And I have the string "my class"
str = "my class"
I can do this:
class_name_from_str = str.title().replace(" ", "") # now class_name_from_str is "MyClass"
How could I make class_name_from_str callable? I want something like this:
callable_obj = some_process(class_name_from_str)
o = callable_obj(arg1, arg2)
o.f()
I'm using Python 2.7, by the way
UPDATE: As Matti Virkkunen suggested in a comment, a good solution is to create a dict to map strings with classes
my_calls_dict = {'my class' : MyClass, 'my other class' : MyOtherClass}
It kind of depends on where your class resides. If it's in the current module
globals()[class_name]
should get you the class. If it's in a module, replace globals() with the module (possibly imported via __import__() - if you want the module name to be dynamic as well).
This would be a good time to think about whether this is a good idea though. Wouldn't it be clearer to, for instance, make a dict of names and corresponding classes? If your .title().strip() thing is part of your actual code, I honestly can't think of what it might be trying to do.