January 30, 2012 § 2 Comments
Last Rails 3.2 post. I swear.
ActiveRecord::Relation#pluck in a previous post because it was one I was particularly excited about. After reading through the rest of the release notes I wanted to highlight a few of the other smaller changes I thought were interesting.
is now written
You can also revert the uniqueness with
Don’t user LOWER for case insensitive searches on MySQL
When querying against our databases there are a bunch of times we don’t care about case. In the case of searching against an email address, a case insensitive search does exactly what we want it to. When you add the
:case_sensitive => true to a
validates_uniqueness_of it use to wrap that part of the query in a
LOWER(). This sucked because it prevents MySQL from using an index. When you put this restriction on your email column and have to do this query every time you save a record. Since MySQL does case insensitive searches, ActiveRecord doesn’t worry about adding the
LOWER() around the columns. Time saved all around!
rake db:drop drops both dev and test
rake db:create creates both the development and test databases. r
ake db:drop now drops the both development and test databases instead of just the development database.
When you have multiple forms on one page for the same type of objects, you can get conflicting ids form elements. Problem solved with form namespaces.
<%= form_for(@offer, :namespace => 'namespace') do |f| %> <%= f.label :version, 'Version' %>: <%= f.text_field :version %> <% end %>
The ids of the form elements have the namespace prepended with and underscore to the front of the original id.
One of the things that I love most about Rails is ActiveSupport. And one of the things I love most about ActiveSupport is how it makes dealing with Time awesome and easy. They have added a few new convenience functions to help with date ranges:
You can use these with ActiveRecord queries like so:
Event.where(:created_at => Time.now.all_week) Event.where(:created_at => Time.now.all_day)
Obviously there are a lot of other additions in 3.2, these are just the few that I know that I am going to have a use for. Enjoy.
November 10, 2011 § Leave a comment
When developing code, there are somethings that you should leave to the experts. Encryption is one of them. When I wrote my encrypted cookies and encrypted cookie sessions gems, one of the the things I didn’t want to do was write any sort of encryption routines. Luckily ActiveSupport has some help in the way of ActiveSupport::MessageEncryptor. It’s used much like ActiveSupport::MessageVerifier which is used for signed cookies in Rails. People much smarter than me have put these pieces together, so it just makes sense to use them. Almost nothing good can come from trying to do this stuff yourself. Here are some examples of a verifier and encryptor being used.
> secret = ActiveSupport::SecureRandom.hex(10) => "379af645b8dcce20b607" > verifier = ActiveSupport::MessageVerifier.new(secret) => #<ActiveSupport::MessageVerifier:0x00000103d7ce50 @secret="379af645b8dcce20b607", @digest="SHA1"> > signed_message = verifier.generate("sign this!") => "BAhJIg9zaWduIHRoaXMhBjoGRVQ=--af1e810b074b1abd6d9dcd775f71b1fafa53c218" # this is "<base 64 encoded and serialized string>--<digest of string>" > verifier.verify(signed_message) => "sign this!" > verifier.verify(signed_message + "alittleextraontheend") ActiveSupport::MessageVerifier::InvalidSignature > verifier.verify("alittleextraatthebeginning" + signed_message) ActiveSupport::MessageVerifier::InvalidSignature
> secret = ActiveSupport::SecureRandom.hex(20) => "c1578de6ec2e1789940729dc9d97b335fc7df588" > encryptor = ActiveSupport::MessageEncryptor.new(secret) => #<ActiveSupport::MessageEncryptor:0x000001295337c8 @secret="c1578de6ec2e1789940729dc9d97b335fc7df588", @cipher="aes-256-cbc"> > encrypted_message = encryptor.encrypt_and_sign("Nothing to see here...") => "BAhJIl9YbDZkK0czS3o0ZkI0Yml6K05uYzgzM05meDJjWWU4QWh0YzdFeFFrbC85b3BocHFORWtRWXdDVWIxaW45TEQ5LS1yVkxGTURJYzFWb2pva0UrVkkwTkFnPT0GOgZFRg==--e61e02a818960d66c7865f5624fad63b1564283f" > encryptor.decrypt_and_verify(encrypted_message) => "Nothing to see here..."
If your secret is too short you’ll get a OpenSSL::Cipher::CipherError. Make sure your secret is at least 32 bytes. Using ActiveSupport::SecureRandom.hex(16) should satisfy this requirement, but obviously longer is better. You can also pass in a :digest => <digest> option as a second argument to both initializers to specify a different algorithm to use.
One of my thoughts is to submit these two gems into Rails so that people don’t make the mistake of trying to roll their own encryption systems for cookies. We’ll see how it goes.
July 28, 2011 § Leave a comment
Dealing with dates is hard. Dealing date calculations is harder. Luckily, in the Rails world we have ActiveSupport to help us with a lot of this. It actually does so much, that I usually forget how much of a pain dealing with dates is suppose to be. However, there are times now and then that I am reminded. Little edge cases that haven’t yet been built into ActiveSupport. One such edge case is determining the number of months between two dates. Why do months have to have different numbers of days?
This particular use case can be very useful when dealing with recurring payments. Calculating the number of payment cycles a subscriber has gone through can tell you how much revenue they have generated. I have put together two class methods for the Time object that will calculate just this. One is a simple loop that takes time proportional to the time between the start and end times, and the other a more efficient direct calculation of the same number.
The simple loop looks like:
def months_between2(start_date, end_date) return -months_between2(end_date, start_date) if end_date < start_date count = 1 while true return count - 1 if (start_date + count.months) > end_date count += 1 end end
It just starts at the
start_date and keeps adding months until it passes the
end_date. Not particularly difficult, but should get the job done for most cases. All the complexity of the more efficient version comes from checks dealing with the various cases arising from different months having a different number of days. Anyways, the source code for that one looks much nicer over on GitHub.