I'm using Ruby 1.8.6 and have a class (not an ActiveRecord class) that I want to cache using memcache-client, which serializes it with Marshal.dump before storing it in the cache. However, it has an instance variable (which does refer to an ActiveRecord class) that I don't want to serialize, as I don't want multiple objects running around corresponding to the same database row. Instead, I want to set the attribute to refer to the appropriate object (which I already have a reference to) after the serialized object is loaded from the cache and reconstructed.
What's the easiest way to prevent only one attribute from being marshalled?
(I'm aware of this question, but the given answer appears to apply only to ActiveRecord classes.)
from http://www.ruby-doc.org/core-1.9.3/Marshal.html
When dumping an object the method marshal_dump will be called.
marshal_dump must return a result containing the information necessary
for marshal_load to reconstitute the object. The result can be any
object.
When loading an object dumped using marshal_dump the object is first
allocated then marshal_load is called with the result from
marshal_dump. marshal_load must recreate the object from the
information in the result.
so the question you are linking to also applies to you. just override those two methods and you should be fine.
Related
I have an object containing sensitive data that I want to marshal (using Marshal) without the sensitive data.
I'd like to be able to say:
def _dump(*args)
# Clean-up sensitive data
super
end
but this produces a 'no superclass method' error. Is there a way I can make my object behave the way I want in response to Marshal.dump, while using the default implementation?
I want Marshal.dump(my_obj) to work out-of-the-box without requiring the API consumer to remember to call a different method.
It may be that there is no superclass method for _dump. If it's defined on your object it's called. If not, the default handler is used.
You probably want to clone your object and remove the sensitive fields, returning that as a Hash inside your _dump function, then undo that within the _load method.
You can also read the documentation on Marshal where it describes the recommended methods.
Sandy Metz says (POODR book, page 26):
Because it is possible to wrap every instance variable in a method and to therefore treat any variable as if it's just another object, the distinction between data and a regular object begins to disappear.
I am not sure if I understand what she is explaining. When we define the accessors, we are wrapping the instance variables (data) on a method but methods are not objects. So what does she mean when she says that we can treat variables as if they're just another object?
The primary difference between data and objects is behaviour. Objects can modify their internal state without changing their interfaces, while data are static structures.
When we wrap data access within a method, we get the same benefits of an object - the interface remains static to consumers even if the underlying data structure needs to change.
I have read allot of questions on here about different ways to initialize a ActiveRecord model properly when dealing with initializing values and is always in relation to providing default values. A great answer I came across helped clarify different ways.
However, if it is frowned upon to override the ActiveRecord base initialize method, what is the proper way to provide parameters to an ActiveRecord model when you want to initialize with values as you would in a standard Ruby class initialize(arg1, arg2...) method.
so you can
obj = MyObject.new(Obj1, some_num)
The only thing I have come across was actually overrideing the initialize method but calling super first in the initialize. However, this was frowned on because ActiveRecord's base class uses allocate in allot of cases to instantiate an AR object and therefore could end up sidestepping the entire initialize method.
So, maybe there is another fundamental reason why I am not finding providing initial values as a standard practice in RoR?
I know I can use validates in the object to validate an object can't be saved without meeting requirements, such as having all the proper attributes set. But I was approaching this particular object, so that it wouldn't be created without the required attributes when it is initialized to begin (since at initialization we would have all that information and if retrieving from the DB we would have all those values).
Can someone help direct me?
extra question
If there is an accepted way to do the above, there seems to be another area that needs to be considered when initializing objects with values and that is when the object is initialized from being retrieved from the DB. Not handling the initializing values properly can possibly override what is retrieved from the DB (or I have read when related to setting attributes to default values). So if that needs to be handled properly, how do we do that?
You can provide a hash of values when you initialize an ActiveRecord object. This is used in the create action of the controller to create a new object based on the data collected in the new action.
For example:
def create
#book = Book.new(params[:book])
where params is a hash of the values for the new object.
When using Hibernate (JPA), if I do the following call :
MyParent parent = em.getReference("myId");
parent.getAListMappedAsOneToMany().add(record)
record.setParent(parent);
Is there any performance problem ?
My thoughts is that getReference does not load the entity and getAListeMappedAsOneToMany().add do not need to load the list as it is defined as lazy fetch.
getAListMappedAsOneToMany could return a very big list if it is really accessed (by calling get or size method).
Could you confirm that there is no performance problem with such a code ?
getReference() doesn't go to the database, and returns a proxy. But if you call a method on the proxy, it initializes the proxy and gets the entity data from the database. So since you call getAListMappedAsOneToMany() on your entity, you don't gain anything by calling getReference() instead of find().
Similarly, the list is loaded lazily. this means that it will only be loaded when you call a method on it. And you do call a method on it: add(). So the data of the elements in the list is also loaded from the database.
Turn on SQL logging in devlopment, to see and understand all the queries executed by Hibernate.
If you want to avoid loading the list, replace your code by
MyParent parent = em.getReference("myId");
record.setParent(parent);
This won't load anything from the database, and it will make the association persistent because Record.parent is the owner side of the association. But beware that this will also make your in-memory object graph inconsistent if the parent has already been loaded before.
getReference() is useful when you don't want to use any members of the object but to give the reference of the object to another object. For example, when entity A referencing entity B and you want to set your b as B of A, then getReference() is what you need.
But in your case, when you get the proxy object, you immediately try to reach one of its members. (aListMappedAsOneToMany) Thus this will result, the whole parent object will be loaded from db.
It is true that, when you getAListMappedAsOneToMany().add(record), it will not load from db yet only if you set inverse="true"
You can learn more information about performance from:
http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/performance.html#performance-collections-mostefficentinverse
Basically, I have an instance of a Ruby object already but want to update whatever instance variables I can from yaml. There is a to_yaml function that will dump my object to yaml. I'm looking for something in the reverse. For example, my_obj.from_yaml(yaml_stuff) and have it update instance variables from the yaml passed in.
Would I need to, in my from_yaml function, use YAML::load and copy each instance variable? Is there a function I can use to quickly copy those variables without much typing if that is the case?
Does Ruby's yaml library have something already where I can pass it the object and the yaml and it'll just do what I want it to do?
Editing for clarity
This is a simple object that will store and load very simple yaml compatible types such as strings and integers.
What I ended up doing
Although I answered this question I wanted to add what I ended up doing, my Object monkey patch
class Object
def from_yaml(yml)
if (yml.nil?)
return
end
yml.instance_variables.each do |iv|
if (self.instance_variable_defined?(iv))
self.instance_variable_set(iv, yml.instance_variable_get(iv))
end
end
end
end
Your question is not clear enough. Which class are you talking about? What kind of YAML documents? You can't have everything serialized to and from YAML.
Let's assume that your object just has a set of instance variables of simple, YAML-compatible types, such as strings, numbers and symbols.
In that case, you can generally, write from_yaml method, which would load YAML file into a hash of key->value pairs, iterate through it and update every instance variable named key with value. Does that seem useful, and if it does, do you need help writing such method?
Edit:
There is no need for you to keep your object state in a hash - you can still use ivars and attr_accessors - just open up a new module (say YamlUpdateable), implement a from_yaml method which would update your ivars from a hash deserialized from YAML, and include the module in whichever class you want to deserialize from YAML.
As far as I know, there's nothing like that included with the YAML library itself; it's mostly meant for dumping and reading data, not keeping it up-to-date in memory and on disk. If you're planning to keep data in memory and on disk synced with each other with minimal hassle, have you considered a data persistence library like ActiveRecord or Stone?
If you're still keen on using the YAML library, and assuming you don't have many different classes to persist, it might make sense to simply write a small "updater" method that updates an object of that class given a similar object. Or you could rework your application to make sure you can simply reload all the objects from the YAML without having to update them (i.e., dump the old objects and create new ones).
The other option is to use metaprogramming to read into an object's properties and update them accordingly, but that seems error-prone and dangerous.
What you are looking for is the merge command.
// fetch yaml file
yml = YAML.load_file("path/to/file.yml")
// merge variables
my_obj.merge(yml)