Joseph Palermo's blog
Ask for Help
"When using the wkhtmltopdf library via the PDFKit gem, if you use an HTML snippet that does not have a height defined, it generates a PDF that appears to only be an image, the text is not searchable or selectable. If you give your element a height however, it behaves as you would expect. Both PDF documents visually look the same though."
Ask for Help
"Trying to write a custom Rails I18n exception handler to catch MissingTranslationData exceptions"
The API didn't seem to do what it claimed it should.
"RubyMine EAP undo behavior seems broken, it only wants to undo a file deletion, and then go back no farther in history."
It was suggested that RubyMine undo is context sensitive, and having focus on the file browser has a different undo stack from the editor window.
"mysql2 gem on Lion is giving us an error: "Library not loaded... Image not found" and then a path pointing down into our RVM directory."
Building the gem with options pointing to your mysql directory was suggested.
Interesting Things
"git checkout -" will toggle you between your last two checkouts (branches or just plain SHA's). Handy if you are switching back and forth. Can't seem to find it mentioned anywhere in the docs though.
Ask for Help
"I have paperclip columns from a custom sql query. Is there any way to instantiate the associated objects just to generate the paperclip urls for those attachments?"
Calling new on the base class and passing in the data does not seem to work.
Interesting Things
The script tab of the webkit inspector has breakpoints for all kinds of events. Is a click event not behaving like you think it should? You can easily check a box and have all click events open the debugger.
Ask for Help
"Why do NULL values get inserted as strings when using mysql LOAD DATA INFILE after dumping the data with mysql -e"
Unfortunately this team only has control over the import, not the existing export. Their only option found was to replace occurrences of NULL with \N after getting the import file.
Interesting Things
A team again reminds us to be aware that the default file system in OSX is case insensitive. They thought this was the reason git would not commit a lowercase file rename for them. However, this setting was later found for your .git/config file.
[core]
ignorecase = false
Ask for Help
"Why is Cucumber clearing our sessions between requests?"
A team is doing Cucumber/WebRat tests of a SAML authentication flow using Devise. No matter what they do, their session seem to be getting cleared between requests. It was suggested to write a vanilla Rails integration test to see if the problem is in Cucumber or the App.
"Any recommendations for a CMS to use with Rails?"
The textarea backed by a single model just isn't cutting it anymore and we probably don't want to end up building our own. Any success or horror stories?
Ask for Help
"S3 is giving us 400 timeout responses on every first upload for a request. All subsequent uploads for the same request seem to work fine."
This turned out to be a bug in the HEAD version of Paperclip. They added a fingerprint method that creates a MD5 hash out of the original file, but the method forgets to rewind the original file when done. So S3 times out while waiting for data, but then rewinds the file before trying again. So it does ultimately get uploaded, but it was leading to ~10 second delays in the middle of the request.
Interesting Things
- Be careful when doing MyModel.find_by_column-that-is-a-string in ActiveRecord. If you pass it an integer it won't do a type conversion and will pass it down to SQL as an unquoted integer. MySQL will then not use any indexes you have on that column. This is with Rails 2.3.x and MySQL 5.0.x
Ask for Help
"Does anybody have solutions for a public sandbox?"
They want people to be able to modify their data, but want the data to roll itself back to the clean state at specific times, or after certain events have happened.
Some people are having problems with the latest RubyMine EAP build. When using our standard keyboard map they are unable to make new lines, they have to use Shift-Enter.
Interesting Things
- People have good things to say about http://html5boilerplate.com/
- A team ran into a problem with https and S3. Using the bucketname.s3.amazonaws.com syntax instead of the s3.amazonaws.com/bucketname syntax can fail when using https if your bucketname has too many segments. This is a known limitation of wildcard certificates.
Ask for Help
"Is there any library for Base64 encoding on the iPhone?"
Interesting Things
Some Pivots created a jasmine-nodejs project to run your Jasmine specs headless using node.js
A team was getting the below error numerous times while running tests. They managed to track it down to a libxml / Nokogiri load order problem. The errors go away if they require libxml before they require Nokogiri.
ruby(59005,0xa0797720) malloc: *** error for object 0x9586b80: incorrect checksum for freed object - object was probably modified after being freed. *** set a breakpoint in malloc_error_break to debug
I recently had a chance to work with RabbitMQ and the AMQP gem.
The first problem we ran into with subscribing to a queue was if we forcefully kill a client, a large number of messages would disappear from the queue (far more than we had processed on the client). I'll get to the "why" of this in a minute, but the solution was simply to turn on acknowledgments (or acks) for our messages which was something we knew we wanted to do anyway.
So after we turned on acks and started processing items from the queue we noticed that the number of items in the queue was not actually going down even though we were correctly sending the ack when we were done processing.
The AMQP gem uses EventMachine under the covers to manage the connections to the RabbitMQ server. It turned out that when you subscribe to a queue, it is a one time thing. You establish a connection and that is it. The server then sends you messages from that queue over the socket. RabbitMQ pre-fetches messages for you, meaning it crams a bunch of data over the socket and doesn't wait for you to ask for more, it notices that you've read data off the socket, and pushes more to you.
The repercussions of this in the EventMachine world is a major blockage of data. EventMachine has an internal loop where it goes through registered sockets, and processes all the data off any sockets that are ready to be read from before it continues its loop. The server was basically keeping the socket full, so EventMachine would only complete a internal loop after we processed a full socket of data, and then it would get blocked on the next loop since the server has already filled the socket up again.
This means that all of our acks were sitting inside of EventMachine waiting for the loop to continue so they could be sent out. It also explains why when we weren't using acks we were losing messages. The server had sent them to our socket and they were waiting to be processed and by killing the process we lost that data.
My first reaction was that the AMQP gem should be pulling all the data off the socket and caching it locally, then processing a single record off of that cache every time the EventMachine loop ran. This of course won't work because as soon as we empty the socket, RabbitMQ is just going to fill it up again (until we have all the messages from the queue in our local cache).
So the solution? RabbitMQ 1.6 has an option to set a pre-fetch limit. So we simply set the pre-fetch limit to 1, and our EventMachine loop runs nice and fast now. You'll want to tweak your pre-fetch limit depending on how long it takes to process each message. If you can churn through a hundred messages a second, you probably won't even notice this problem and the prefetching will help you, but if it takes you a few seconds (or minutes) per message, you'll wonder why things aren't popped off the queue for several minutes (or hours).
You may have heard of some problems we've had with changes to named_scope in Rails 2.3.
The basic change is that when chaining named scopes together, their scoping does not apply only to the finder class, but also to any lambdas evaluated farther along the named scope chain.
So given a User class with a friends association (pointing at other Users) with the following named_scopes:
named_scope :named_bob, {
:conditions => {:name => 'bob'}
}
named_scope :second_degree_friends, lambda{|user|
user_friends = user.friends
second_degree_friend_ids = user_friends.collect{|u| u.friend_ids}
{
:conditions => {:id => second_degree_friend_ids.flatten}
}
}
These two calls are no longer the same.
User.second_degree_friends(user_sam).named_bob
User.named_bob.second_degree_friends(user_sam)
The first call does what we expect (giving us all of user_sam's second degree friends who are named bob. But the second call actually gives us something different. Because the named_bob scope comes first in the chain, when it evaluates the lambda for second_degree_friends, it applies it in the scope of all previous named scopes. So our call to the user.friends association is actually scoped with the additional condition of :name => 'bob', which is probably not what we want in this case.
You can see the lighthouse ticket where I claim this should not be the default behavior of named scopes. But my question right now is, "How do you use named scopes?"
I tend to use them in a composable manner, especially in search objects. I take a base finder such as User or User.friends and then I pass it down to a add_conditions or add_sort method. Inside those methods, they add on any other named scopes they need to and return the new finder object. So inside of this chain, you never really know what finders have been applied already, but in the past, you didn't need to know because the same named_scope with the same parameters always gave you the same conditions.
Often there will be one search object that inherits from another, say for instance LocationUserSearch < UserSearch that adds geo targeted searching on top of UserSearch. In these cases, we can just create our own add_conditions method, call super and tack on any new conditions that we need. Since conditions and joins are merged in scopes, this normally works out great.
Do you use named scopes in a composable way such as this? Or do you only combine them in a known way and might benefit from having the accumulated scope applied to the lambda?
Feel free to add your comments to the lighthouse ticket too.
