Forums | Admin

Discussion Forums: open-discussion

Start New Thread Start New Thread

 

By: David Parrott
RE: Capitalized hash keys and Rails [ reply ]  
2007-08-27 23:37
Did some more research, and this is simply a bug in rails:
http://dev.rubyonrails.org/ticket/8958

I also have two easier work-arounds (for use until rails releases the referenced patch) that don't require any complicated downcasing of hash keys. The basic idea is to wrap the hash inside another object.

Option 1, we just wrap the hash with an array:

@hits = @mturk.searchHITsAll.collect {|h| [h]}

With this option, your partial looks a bit odd, because you have to use hit[0] instead of just hit.

Option 2, wrap the array in a custom class (stick the definition in application.rb)

class Delegate
def initialize(o);@o=o;end
def method_missing(*a);@o.send(*a);end
end

@hits = @mturk.searchHITsAll.collect {|h| Delegate.new(h) }

This lets you have the normal-looking partial.

By: David Parrott
Capitalized hash keys and Rails [ reply ]  
2007-08-20 19:48
Some users have run into a problem with integrating ruby-aws and Rails. It turns our that most of the MechanicalTurk calls generate responses with capitalized keys ( :HIT, :HITTypeId, :NumAssignmentsAvailable, etc ). This is all well and good until you try to pass that into Rails' render :partial function. Rails tries to 'automagically' convert the nested hash into a full-blooded object and chokes on the capitalized keys.

now, this use case is supported neither by ruby-aws nor rails. I quote, from their current source code documentation:

# NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also just keep domain objects,
# like Active Records, in there.

And looking into the code further, it looks like trying to do this matchup it just asking for something to break. Interestingly, as long as none of the keys in the hash have a capitalized letter, it seems to work with the current version of Rails.

So, what's the solution? Is it acceptable to pipe one webservice's results directly into your render :partial call? Are there better solutions with trivial implementation cost?

It seems to me that some simple translation code would get this working on the current version of Rails, but it seems like a really bad idea from a style and maintenance point of view.

Copying from comments in a bug report we received, assuming you were previously doing something like this:

render :partial => 'assignment', :collection =>
results[:Assignment]

instead try:

assignments = results[:Assignment].collect {|assignment|
Struct.new( *( assignment.keys.collect {|key|
key.to_s.downcase.to_sym }
).new( *assignment.values ) }
render :partial => 'assignment', :collection =>
assignments

but that's not pretty code. We are tentatively planning to add an initialization option to ruby-aws which would downcase all keys in return values, so you didn't need that mess if you want to ignore my advice and continue piping directly from your webservice to your render call.

For those of you with more style, I would recommend implementing a Model object which represented the data you wanted to retrieve and have that model populate its own information using ruby-aws. This would be much cleaner and seems like way better MVC design.

Thoughts, comments, flames? Anyone know a bit more about the Rails guys' opinion on what you should be passing into your render :partial calls?