If you have ever worked with polymorphic associations you know that using them in a search can be a pain in the ass. Rightfully so, because a polymorphic association is ambiguous, in order for ActiveRecord to do its magic with creating joins it needs more information that it can’t get. Such as, are you wanting to search type A or type B? In my projects I’ve been dealing with this by writing my own named scopes with my own joins. Not a big deal. But here’s the problem….
My favorite feature in Searchlogic is named scope delegation. I’ve noticed in a number of projects I’ve seen where programmers copy over named scope logic into related models. Which makes sense, because AR gives you no way of easily accessing related scopes. For example:
Order.named_scope :pending, :conditions => {:state => "pending"} User.named_scope :pending_orders, :joins => :orders, :conditions => "orders.state = 'pending'"
Most people would look at that and never see anything wrong with it. What is the conditions for a pending order changes?
Order.named_scope :pending, :conditions => "orders.state = 'pending' AND approved = true
Now you have to remember to change this logic in associated models. This is a programming no-no. With searchlogic you don’t even need to touch the User model. You can access the order’s named scope, and it delegates that call:
User.orders_pending
Now when you change what defines an order as pending it will change everywhere in your application. This is great, but where this feature comes to a screeching halt is when you encounter a polymorphic association. This is because AR has no internal support for this either. For example, you can’t do:
Audit.belongs_to :auditable, :polymorphic => true User.has_many :audits, :as => :auditable Audit.all(:joins => :user)
Obviously the above won’t work. AR has no idea that there is a relationship with users through the polymorphic auditable relationship. To overcome this you have to write your own join. Again, there is nothing wrong with writing SQL, I don’t want to send the wrong message. The problem is that you are back to square one with my named scope delegation problem above. To solve this I added a way to overcome this. With searchlogic you could do this:
Audit.auditable_user_type_name_like("ben")
Searchlogic will take care of creating the join on the polymorphic relationship and then delegate the call to the User.name_like named_scope. I think it’s pretty nifty.
I hope I explained this well. I figured I would mention the new feature because I’m sure I’m not the only person that has encountered this problem.
For those of you that don’t know, geokit is a great little gem that allows you to geocode addresses. It adds in very easy to use options in the ActiveRecord find method. Ex:
# will return all venues within 10 miles of the 10018 zip code Venue.find(:all, :origin => "10018", :within => 10)
The problem is that you can’t use geokit options in a named scope. Since searchlogic is named scope driven this presented a problem. So I wrote a little module that transfers geokit options from named scopes to the actual arguments of the method:
module GeokitForNamedScopes OPTIONS = [:origin, :within, :beyond, :range, :formula, :bounds] def find(*args) super(*transfer_from_scope_to_args(args)) end def count(*args) super(*transfer_from_scope_to_args(args)) end private def transfer_from_scope_to_args(args) find_options = scope(:find) if find_options.is_a?(Hash) options = args.extract_options! OPTIONS.each do |key| options[key] = find_options.delete(key) if find_options.key?(key) end args << options else args end end end class ActiveRecord::Base class << self def acts_as_mappable(*args) result = super(*args) extend GeokitForNamedScopes GeokitForNamedScopes::OPTIONS.each do |key| named_scope key, lambda { |value| {key => value} } end result end VALID_FIND_OPTIONS += GeokitForNamedScopes::OPTIONS end end
It’s far from perfect. The glaring problem is obviously redefining the VALID_FIND_OPTIONS constant, which throws a warning. But it works and it’s good enough for now. If anyone has any suggestions on how to improve this I’d to hear them. But you should be able to use geokit options in a named_scope with the above code. Ex:
Venue.origin("10017").within(10).all # => will return all venues within 10 miles of 10017
Geokit doesn’t really support making these calls through associations. But if you read the documentation. You will notice they do support this:
acts_as_mappable :through => :whatever
You can use that feature to bridge the gap
Hopefully this helps some of you.
I know this post is kind of out of the norm, but what the hell, I figured I would let it fly. Today I was thinking a lot about business and what makes a company successful. I was thinking specifically about marketing and I came up with an interesting scenario:
Say you had money to invest and you had to pick one of these companies to invest in. Both companies are in new industries that have pretty good potential for growth. Which one would you pick?
The above facts are set in stone. You can’t say you would change their marketing strategy or improve their product. You had to pick one of the companies given the current facts above, with no assumptions as to what can or might happen in the future.
I am also aware that if you were actually investing into a company you would require much more information, and there are a number of factors that would sway your decision. But setting that all aside, and you had to pick one right now given the above information, which one would you pick?
I figured I would post an update keeping everyone in the loop. I created a new branch for authlogic locally and I’ve been making some changes mentioned in this post. Some of the changes I’m making are still somewhat experimental and I’m playing around with the code to see if I like the end result. Figuring out a way to make this ORM agnostic has proven difficult, simply because there is no standard. They all follow a similar pattern, but the method naming conventions and some of the feature implementations are vastly different. I’m also not sure you could even set a standard. Regardless, I do think this is an important feature since there is a lot of movement around alternative storage solutions such as MongoDB, CouchDB, etc.
Splitting out some of the authentication code into “authenticators” has worked out great so far. More importantly, its more or less a blueprint for people to extend Authlogic and provide alternate authentication solutions (openid, facebook connect, oauth, etc.).
The last major change I’ve been making is removing validations in the acts_as_authentic module. Validations add some clutter to authlogic, some might say its a necessary clutter, but I’m leaning towards leaving it out. Validation is such an easy thing to do. ActiveRecord provides very simple methods for doing this, I also feel like its gets in the way for certain edge cases. Authlogic will still provide some nice methods to make your life easier, like validating the format of an email address, the main difference being that you have to explicitly implement this validation. No validations will be automatically added. Lastly, this makes ORM abstraction a little easier.
So that’s that. I’ll keep you updated when I start to push out some code and what not. I’m probably going to label this release as v3, because of the validation changes and some of the other major changes that might break backwards compatibility.
Lately I’ve been really going back and forth between using tools like resourcelogic, inherited_resources, resource_controller, etc. And I gave each one of these libraries a more than fair shot. I’ve used all of them extensively, that’s the whole reason I created resourcelogic to begin with. I really liked resource_controller, so much that I decided to take it and kind of make my own version called resourcelogic. I ended up changing it quite a bit, and it worked out well, but it got out of hand. So I’m going to address why I advise against using libraries like this:
(more…)
I wanted to make a post about this. I’ve been extremely busy with my job lately. I’m launching a new company, and as we approach launch date it seems to get more hectic. I’ve got quite a few messages in my github account, email, google groups, etc. I usually try to block out about an hour a day answering emails and performing basic chores with my open source projects, but I literally haven’t had an hour to spare. The time that I do have is spent maintaining my open source projects, so solving individual problems has been put on hold for now. I apologize if I haven’t responded to you. Don’t take it as an insult, I am just very busy right now. If you need help, please use google groups, the people on there are very helpful. By the way, thanks to everyone on google groups, github, Ryan Bates on railscasts, etc, for helping out. You guys really have been a big help, this is why I love open source.
A few days ago I released a feature that Ryan Bates requested: combining named scopes with ‘OR’. As you know, when you combine named scopes they join the conditions with ‘AND’. This obviously makes sense, but what if you want to combine conditions with ‘OR’? With this new feature you can do the following:
User.first_name_or_last_name_like("ben") User.id_lt_or_age_gt(10)
I think this is nice feature on a variety of levels, but what I particularly like is the implementation. (more…)
This week my task at hand was to figure out a deployment strategy for an application I’ve been working on. The initial conclusion was that we needed to buy another server. I wasn’t too happy with this because setting up a new server is a pain in the ass. It’s very repetitive, mundane, and boring. I’d much rather be spending time coding or coming up with new ideas instead of maintaining servers.
That being said, I figured I would see what engine yard or heroku had to offer. Maybe it’s time my company starts using these services. It would be easier and cheaper. (more…)
So I’ve been thinking about Authlogic lately and here are a few ideas I’ve been bouncing around:
The worst part about this idea are the views. Interfaces can be quite a bit different from application to application. How do I provide views that are suitable for every project? In my opinion you can’t. I could give you a million partials, tons of configuration, a lot of helpers, etc. This might add some flexibility, but this seems like a hack. It’s not a clean solution. This is the one thing that I just don’t like about rails engines. I prefer tools that help me create my views. Give me a set of tools so I can go easily create my interface, but don’t go create my interface for me. And I think rails does a pretty good job of this with things like form_for, etc.
The bottom line is that I want to keep Authlogic focused on the business logic behind authentication. You should be able to use Authlogic in a rails app, merb app, sinatra app, etc. All of the interface cruft should probably be in a separate gem or in a template. The thing is, this gem could be written a million different ways depending on your preferences. Maybe I can create a base rails engine that people can fork and modify to their liking. That’s the beauty of git.
To conclude, #1 is probably going to happen, I want to do #2 if I can figure out a good way to do this, #3 is still up in the air and more likely to be in a separate gem / plugin.
That’s it for now, I figured I would try to keep everyone in the loop. Maybe you can help me out or have some ideas of your own.
Today I released the first version of Shippinglogic, which is a ruby library I wrote to make integrating with FedEx, UPS, USPS, etc, very easy.
This is not a project I wanted to do, but I couldn’t find a library that did everything I needed it to. One library had support for creating shipments, but didn’t have support for getting a list of rates. Another had support for getting rates, but didn’t have support for creating shipments. I need both for the application I’m working on. I even decided to modify one of the libraries to suit my needs but the code was really hard to follow and I didn’t feel comfortable using it. Especially since shipping was such an important part of my application.
That’s when I came to the sad realization that I probably need to write my own library. (more…)