April 8, 2015 by Daniel P. Clark

ActiveRecord vs Me… Round 1 – FIGHT!

I’ve done a fair bit of work in creating a library that integrates with ActiveRecord Objects.  I also have a pretty good grasp on mixins/inheritance.  Yet I still run into situations where trying include, extend, and techniques for monkey patching just don’t work out.

I was trying to implement a method like first_or_create on the ActiveRecord::QueryMethods::WhereChain, and/or ActiveRecord::Relation, and possibly write the appropriate delegation on ActiveRecord::Querying.  I literally spent many hours without success.  I even tried ActiveRecord::Base (which isn’t what I need) simply because I have had success with that for something else.

The way I was looking in to doing it was by first going to the Rails source code and seeing how first_or_create was implemented.  The previous modules and classes I’ve mentioned all have something to do with it.  But with lots of effort and no success I must say it was very frustrating experience.

A Working Solution

So the method I was trying to implement is called first_or_force_create.  It’s not really a method you want lying around for other people to use because it bypasses validations and could be a potential mess waiting to happen.  But I did want to implement it as a tool I could use when needed for the rare case I’ve come upon.

I implemented it by creating a lambda to be passed into, and evaluated within the context of, the WhereChain to allow the where clauses to be attributes on what’s created.

First I create a file in the lib directory first_or_force_create.rb .

I may move it into a sub-folder, maybe call it hack or monkey_smashes.  Also I may scope it within a module to not pollute the main name space with, potentially many, lambda methods.

Use Case: I needed a field to be nil that would always be validated as not nit otherwise.  I have a messaging system using the ActsAsMessageable gem which will only send messages between ActiveRecord objects of the same Class and record type.  So the User model will have many Identities (has_many :identities) and Identity (belongs_to :user; validates_presence_of :user_id) is the ActiveRecord object that we add ActsAsMessageable to.  So Identities may now send messages between each other and they require Identities for both to and from.  But I want to integrate incoming SMS messages into the messages.  So I need to implement an Identity that doesn’t have a User as an owner.  It will be the “Incoming SMS” Identity.

This can now be the way I create the identity to message with and use it with my incoming SMS API. Notice there’s no user_id: parameter.  This command creates a new Identity record with the name field as “Incoming SMS” and saves it without validation.

What’s important here is the way I’m passing the lambda in.  You see that I’ve written instance_exec with that ampersand (&) in front of the lambda method?  The instance_exec method will permit arguments to be passed (should you need to); instance_eval will not.  Passing a lambda with the ampersand both calls to_proc (the & part) on the lambda and the instance_exec uses the content of the lambda as the block (Proc) to execute.

You cannot use .send to send the lambda in because you would need to execute the lambda with one of its .call methods.  And when you perform a .call in the context of .send it evaluates the lambda outside the Object before actually sending it in.  In this case it will give a ‘NoMethodError `first’ for main:’ because lambda’s version of self is evaluated by the external context in which it is called.  So you will need to evaluate the lambda inside the Object you wish to use it on with something like instance_exec &MyLambda .

Summary

Making a generally bad idea method to be used in an unconventional way may be a good strategy to help prevent amateurs from using it.  You wouldn’t want your checks and balances in your code bypassed by everyone, that defeats the purpose.  So having a stash of lambdas at hand could be nice for one-offs.  Just be mindful that if you leave an instance of it being used in the code base some one might try to mimic it. *frown* 🙁

Also if you know of a better way to do any of what I’ve mentioned above I’d love to hear it!  Whether it’s adding a method to work in the same way as first_or_create that works on a WhereChain, or it’s implementing ActsAsMessageable to work with other Objects and interfaces like incoming SMS.

There’s nothing quite as frustrating as knowing how things generally work and spending hours without success.  So I wanted to at least spare some some potential headaches by providing something that works.  If you want to implement something on ActiveRecord instances and scopes that’s easy, you can see how I’ve done it on my gem PolyBelongsTo with ActiveRecord::Base.  Working on the WhereChain is a whole other can of beans.  It may be easy, but not without the proper understanding of it; which I don’t have as of yet.

Passing lambdas as blocks of code to be evaluated inside some other context is hugely powerful.  They’re very much worth looking into.  Even if you know them, there’s ‘always’ more to learn.

Hopefully you found this both helpful and inspirational.  Anything to help the mind thrive.  Feel free to inform me on anything I’m mistaken about; I’d love to know!  Please feel free to comment, share, subscribe to my RSS Feed, and follow me on twitter @6ftdan!

God Bless!
-Daniel P. Clark

Image by Sumeet Moghe via the Creative Commons Attribution-NonCommercial-ShareAlike 2.0 Generic License

#activerecord#instance_exec#lambda#rails#ruby on rails#WhereChain