The two obvious issues are that
(1) I'm serializing a hash of 15 objects that contain seven strings
apiece. The objects were Structs, but in the interest of pulling the
definition out of a module and putting it in app/models, I've since
turned it into a non-AR Class because I thought maybe the issue was
that the Struct was defined in a library module.
models/idx_row.rb:
class IdxRow
attr_accessor :image_url, :price, :beds, :baths, :sqft, :mls_id,
:mls_url
def initialize
@image_url = ""
@price = ""
@beds = ""
@baths = ""
@sqft = ""
@mls_id = ""
@mls_url = ""
end
end
(2) The model I have the serialized attribute on is the Comment class
created by acts_as_commentable. The basic class definition is in the
plugin, not in app/models. I looked through the code and it looked like
the proper way to extend the class was through a module of my own, so I
created lib/commentable_extensions.rb and put it there. I added a TEXT
column called "feed", and wrote the following:
lib/commentable_extensions.rb:
module CommentableExtensions
module Juixe
module Acts #:nodoc:
module Commentable #:nodoc:
module ClassMethods
def acts_as_commentable
serialize :feed
end
end
end
end
end
end
I've tried a few ways of loading the extension, currently an include in
application.rb, and when I load a Comment into c and assign one of my
hashes to c.feed, I get the same old SystemStackError when I attempt
c.save:
SystemStackError: stack level too deep
from c:/ruby/lib/ruby/1.8/yaml/rubytypes.rb:168:in `to_yaml'
from c:/ruby/lib/ruby/1.8/yaml.rb:387:in `quick_emit'
from c:/ruby/lib/ruby/1.8/yaml/rubytypes.rb:164:in `to_yaml'
from c:/ruby/lib/ruby/1.8/yaml/rubytypes.rb:41:in `to_yaml'
from c:/ruby/lib/ruby/1.8/yaml/rubytypes.rb:40:in `to_yaml'
from c:/ruby/lib/ruby/1.8/yaml/rubytypes.rb:39:in `to_yaml'
from c:/ruby/lib/ruby/1.8/yaml.rb:387:in `quick_emit'.....
Though it's possible that my object is simply too complex for
#to_yaml's tiny brain, I'm reluctant to think that's the case with what
seems to me such a simple data structure. At this point I'm wondering
if the problem is the way I'm trying to declare the serialization of
:feed. Is there some other place or way I'm supposed to do this?
Thanks.
Either I'm missing something obvious, or this is kinda lame. I saw a
closed ticket in Rails Trac from two years ago in which I think Jamis
submitted a patch that extended AR Serialize to use Marshal and a
binary column optionally in place of YAML because of this very sort of
#to_yaml explosion whenever it was asked to do anything meaningful.
Anyone know if this is present and undocumented, or something that
fell by the wayside?
That is, I cannot say:
r = IdxRow.new
r.price = "$300"
c.feed = r
c.save
...but I can say
x = Struct.new(:foo, :bar, :baz, :feh, :fez, :fex)
r = x[1,2,3,4,5,6]
c.feed = r
c.save
Nope. AR Serialize exploded like it always does when given anything
more complex than a one-liner.
Also, attempting Marshal.dump on this hash of hashes gives me an error
message that helpfully tells me I can't dump a Proc. Which would be
more helpful if I could see how a vanilla hash of vanilla hashes of
vanilla strings is being seen as containing a Proc.
hatless wrote:
> I keep narrowing it down. Now I've banished my nice Struct and the
> less-nice custom IdxRow class i created in its place, and instead I'm
> now putting my data into a single hash containing 15 hashes of 7
> strings apiece. No Procs in sight, nothing fancy, just a hash of hashes
> of strings. I figured this would work for sure.
>
> Nope. AR Serialize exploded like it always does when given anything
> more complex than a one-liner.
>
> Also, attempting Marshal.dump on this hash of hashes gives me an error
> message that helpfully tells me I can't dump a Proc. Which would be
> more helpful if I could see how a vanilla hash of vanilla hashes of
> vanilla strings is being seen as containing a Proc.
Does your Hash have a custom contructore with a Proc? ie:
>> h2 = Hash.new{ |h,k| h[k] = 5 }
=> {}
>> Marshal.dump( h2 )
TypeError: can't dump hash with default proc
from (irb):6:in `dump'
from (irb):6
from :0
You can't do that =)
Zach
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.3 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFFdz4QMyx0fW1d8G0RAg+IAJ94AxFW2HEi6nMmmydKzL/f7vSr3gCfRo2t
eBFONZp45kozWKjOfRvJnsg=
=gYhS
-----END PGP SIGNATURE-----
myhash = {}
i = 1
for row in [1,2,3]
temphash = {}
temphash['foo'] = "bar"
myhash[i] = temphash
i += 1
end
This code works. My actual code, which assigns string values to a bunch
more elements of the inner hashes the same way makes #to_yaml and
Marshal.dump() break.
The generated Hash and its members don't contain any object
identifiers. There are no custom constructors. Like the above code, the
real method results in an ordinary looking hash with integer keys and
Hash values, and inner Hashes with String keys and values.
Here's my actual code that returns the hash. I've only changed the
names of my keys so I don't give away whose HTML I'm scraping:
def fetch_idx_hash(obj)
counter = 1 # 1..n instead of starting with zero, used as the hash
index.
myhash = {}
page = fetch_page(obj)
soup = BeautifulSoup.new page
soup.html.body.center.find_all('table')[5].find_all('tr').each do
|tr|
working_row = {}
if tr.find_all('td')[1].string.include? "$"
working_row['price'] = tr.find_all('td')[1].string
end
unless tr.find_all('td')[0].a.nil?
working_row['fruit'] = tr.find_all('td')[0].a.string
working_row['vegetable']= "http://www.foo.com" +
tr.find_all('td')[0].a['href']
end
working_row['starch'] = tr.find_all('td')[3].string
working_row['meat'] = tr.find_all('td')[4].string
working_row['cake'] = tr.find_all('td')[5].string
# This must be the last member populated. Rows w/o images get
discarded.
unless tr.img.nil?
working_row['image_url'] = tr.img['src']
myhash[counter] = working_row
counter += 1
end
end
return myhash
end
Calling to_yaml on the resulting hash blows up with a SystemStackError.
Calling Marshal.dump() carps about a Proc. I'm wondering if after
everything I've done to simplify the data structure, it's just too big.
Incidentally, none of the member strings exceed 300 characters, and
most are small. A typical myhash.to_s.size is about 7K.
Turns out some of the methods in RubyfulSoup -- the RubyfulSoup#string
method, in fact -- don't return Strings. They return NavigableStrings,
as I discovered when I ran
myhash[1].values.each {|v| puts "#{v}: #{v.class}"}
All was good once I changed things like:
working_row['fruit'] = tr.find_all('td')[0].a.string
to
working_row['fruit'] = tr.find_all('td')[0].a.string.to_s
ARGH!
Now myhash serializes fine. I'm surprised it doesn't seem to
deserialize automagically and I have to YAML.load() it, but that's for
another thread (or not).
It didn't help that the culprit objects rendered like normal Strings.
Thanks again for the help and thanks to anyone sitting through a
5-message thread of me talking myself through it.