Nested to_xml issue? - ruby-on-rails

This may be a basic question but it has been causing me some problems. I am trying to dump an ActiveRecord Object to an XML file using the to_xml function. For whatever reason, this does not work for me if I try to nest it into an element.
Basically I have a hash of ActiveRecord objects that I want to iterate over, and then dump into my XML file like this:
#hash_of_activerecord.each do |key, value|
xml.object do
value.to_xml
end
end
For whatever reason this does not seem to work. What can I do to fix it? Obviously I could just print out each aspect of the object individually but that is not the best solution because I would have to remember to change what is in that loop if I later made a change to the contents of that ActiveRecord object.

Use :include. See http://apidock.com/rails/ActiveRecord/Serialization/to_xml

Related

Ruby on Rails - using a block parameter as a method call

I'm having trouble with a little Ruby on Rails I'm building and need some help.
I have a Table with 20+ Columns and a corresponding XML File which can be parsed as some sort of hash with a gem. Every key would be mapped to a column and every value would be a data record in said column.
The way I access a specific value in the already parsed XML file is:
filename["crs","inputkeyhere"]
which returns the value, for example "52" or whatever.
What I am trying to do is upload the file, parse it with the gem and give each column the corresponding value.
My table (or model) is called "Attributeset" and I already know how I can access every column:
#attributeset = Attributeset.new
#attributeset.attributes.keys
So my thought process was:
Iterate over all the keys
Pass every key into a block called |a|
Use the rails possibilty to set attributes by calling the corresponding #attributeset.
Set colum attribute to the corresponding xml key
So my code would go something like this:
#attributeset.attributes.keys.each do |a|
#attributeset.a=filename["crs",a]
end
But my problem is, that ruby thinks ".a" is a method and apparently does not evaluate "a" to the block parameter.
I've read through lambdas and procs and whatnot but didn't really understand how they could work for my specific situation.
Coming from bash scripting maybe my thinking might be wrong but I thought that the .a might get evaluated.
I know I can run the block with yield, but this only works in methods as far as I know..
Any help is appreciated.
Thanks and stay healthy,
Alex
Thanks for the input!
I wanted to make it as clean as possible, and not using any temporary hashes to pass arguments.
I've found the method
write_attribute
which can be used like this:
#attributeset.write_attribute(a, xmp["crs",a])
worked perfectly for me.
You can use []= method to set values dynamically:
#attributeset.attribute_names.each do |attribute|
#attributeset[attribute] = filename["crs", attribute]
end

How to unfreeze the Hash?

I have an object from GraphQL. I need to process one field and then return a Hash.
To do this I initially tried using the to_h method. But when I tried to change the value in the field, I got this error:
FrozenError - can't modify frozen Hash
Then I came up with the idea of using the dup method. This method seems to "unfreeze" the original object:
object.to_h.dup
But this doesn't help for "deep" fields. For "deep" fields I had to use the local dup method.
Please tell me if there is a more correct solution to the problem?
Rails (or ActiveSupport if you want to be pedantic) patches a deep_dup method into Hash which does a deep copy. So you should be able to say:
object.to_h.deep_dup
to get a fully mutable version of your Hash.

Read json serialised objects back from a file

I am aiming to serialise a set of objects into a file so as to create a backup. I have the start of that working, using a methods on the models (simplified here, assuming I have two ActiveRecords foo and bar):
def backup(file, foo, bar)
file.write(foo.to_json(root: true))
file.write(bar.to_json(root: true))
end
This gives me a file as I desire, in this case with two records:
{"foo":{"Account_id":1,"Name":"F","created_at":"2013-04-16T10:06:19Z","id":1,"updated_at":"2013-04-20T11:36:23Z"}}
{"bar":{"Account_id":1,"Name":"B","created_at":"2013-04-16T10:06:19Z","id":1,"updated_at":"2013-04-20T11:36:23Z"}}
At a later date I then want to read that backup in and reinstantiate those objects, probably then persisting them back to the database. My aim is to iterate through the file checking the type of each object, then instantiating the right object.
I have part of the logic, but not yet all of it, I haven't worked out how I determine the type of each serialised object before I instantiate it. The code I have for a restore is as follows:
def restore(file)
file.each_line do |line|
**<some magic that parses my line into objectType and objectHash>**
case objectType
when :foo
Foo.new.from_json(objectHash)
Foo.process
Foo.save!
when :bar
Bar.new.from_json(objectHash)
Bar.process
Bar.save!
end
end
end
What I'm looking for is the bit that goes in the "some magic" section. I can just write the code to parse the line directly to determine whether it's a foo or a bar, but I feel like there's probably some tricky Rails/Ruby way to do this that is automatic. Unfortunately, in this case Google is not being my friend. All I can see are pages that are focused on json in the web requests, but not parsing json back in this way. Is there something I'm missing, or should I just write the code to split the string directly and read the object type?
If I do write the code to split the string directly, I would write something along the lines of:
objectType = line[/^{"(\w*)"=>(.*)}/, 1]
objectHash = line[/{"(\w*)"=>(.*)}/, 2]
This is pretty ugly and I'm sure there's a better way (which I'm still looking into), but I'm not sure that this is even the right approach v's there being something that automatically looks at a json representation and knows from the root value what object to instantiate.
Lastly, the actual instantiation using from_json isn't working either, it isn't populating any of the fields on my ActiveRecord. It gives me nil parameters, so I think the parse syntax isn't right.
So, that makes three questions:
Is there a way to determine which object it is that I'm just missing, that is much cleaner?
If there isn't and I need to use a regexp, is there a syntax to get both bits of the line parsed in a single go, rather than my two lines with the same regexp?
The from_json syntax appears unhappy. Is there a syntax I'm missing here? (no longer a question - the code above is fixed, I was using as_json when it should have been to_json, although the documentation is rather unclear on that....)
(Note: edits over time to clarify my question, and because I've now got a regexp that works (didn't before), but still not sure it's very elegant.)
Further information - one of the problems here, as I dig into it further, is that the as_json isn't actually giving me json - what I have in the file is a hash, not json at all. Further, the values for created_at and lastupdated_at in the hash aren't quoted - so basically that's what's causing the parse on the way back in to fail. I've worked out that I should use to_json instead of as_json, although the documentation suggests that as_json should work.
I'm not sure I fully understand you're methodology, but I think using JSON.parse() would help.
There's some good information here http://mike.bailey.net.au/2011/02/json-with-ruby-and-rails/
This would help you translate the raw object back to a hash.
OK, so I think I've got something that works. I'm not convinced at all that it's elegant, but it gives me the result. I'll spend some time later trying to make it cleaner.
The code looks like this:
file.each_line do |line|
objectType = line[/^{"(\w*)":(.*)}/, 1]
objectJSON = line[/{"(\w*)":(.*)}/, 2]
objectHash = JSON.parse(objectJSON)
case objectType
when 'foo'
restoredFoo = Foo.new(objectHash.except('id', 'created_at', 'updated_at'))
restoredFoo.created_at = objectHash['created_at']
restoredFoo.updated_at = objectHash['updated_at']
restoredFoo.save!
end
when 'bar'
restoredBar = Bar.new(objectHash.except('id', 'created_at', 'updated_at'))
restoredBar.created_at = objectHash['created_at']
restoredBar.updated_at = objectHash['updated_at']
restoredBar.save!
end
end
Items of note:
I feel like there should be a way to create the object that isn't a JSON.parse, but rather would make use of the from_json method on the model. I'm not sure what the from_json is good for if it doesn't do this!!
I'm having fun with mass_assignment. I don't really want to use :without_protection => true, although this would be an option. My concern is that I do want the created_at and updated_at to be restored as they were, but I want a new id. I'm going to be doing this for a number of entities in my application, I didn't really want to end up replicating the attributes_protected in the code - it seems not very DRY
I'm still pretty sure my reg exp can give me both objectType and objectJSON in one call
But having said all that, it works, which is a good step forwards.

rails using .send( ) with a serialized column to add element to hash

I'm serializing many attributes on a model Page as hashes.
Because of the high number of attributes, I've taken a meta-programming approach and want to use .send() to iterate through a collection of attributes (such that I don't have to type out an update action for each attribute.
I've done something like this:
insights.each do |ins|
self.send("#{ins.name}=", {(Time.now) => ins.values[1]['value'].to_f})
self.save
end
The problem is that this obviously overwrites the whole serialized column, whereas I wish to add this as an element to the serialized hash.
Tried something like this:
insights.each do |ins|
self.send("#{ins.name}[#{Time.now}]=", ins.values[1]['value'].to_f)
self.save
end
But get a NoMethodError: undefined method page_fan_adds_unique[Mon Aug 13 13:31:58 -0400 2012]=
In the console I'm able to do Page.find(5).page_fan_adds_unique[Time.now]= 12345 and save it as an additional element to the hash as expected.
So how can I use .send() to save an additional element to a serialized hash? Or is there some other approach? Such as using update_attribute or another method? Writing my own? Any help is appreciated, even if the advice is that I shouldn't be using serialization for this.
I'd do :
self.ins.name.send(:[]=, key, value)

How can I get YAML::load to call const_missing?

I am serializing an object to a database field using ActiveRecord's :serialize functionality in Ruby on Rails
class DrawElement < ActiveRecord::Base
...
serialize :content
end
The reason I'm serializing the objects is that I'm dynamically loading the types from disk using const_missing, so I don't have to setup database tables for them.
def DrawElement.const_missing(const)
require File.join('draw_elements',const.to_s)
draw_class = const_get(const)
return draw_class if draw_class
raise "Draw Element not found #{const.to_s}"
end
So when I want to add a draw element, I do something like this in irb
draw_element.content = DrawElement::Text.new
Everything here works fine
The problem is that when I try to load the object from the database in a fresh session, YAML::load never calls const_missing to require the class definition before loading the file. So all my #content objects come back as YAML::Object
Is there a better way to do this? I'm trying to be able to add new types without having to change the database, or have a has_many_polymorph relationship between DrawElements and a Document.
Ruby on Rails v.2.3.8, Ruby v. 1.8.7
From my experience YAML::load returns a hash. It's up to me to walk through the hash and do something with its contents. Neither load or load_file accept a block to get inside them and influence how the YAML document is parsed.
You could try messing with load_documents or each_document though, because they take a block, but I don't know if you could add additional hash elements that way.

Resources