Friday, August 8, 2014

building a live search feature using Ajax: Part 1 (the model)

I am building a reddit-like social network for Tealeaf Academy's Ruby on Rails course.

Each post on my site describes an online resource. Each post consists of a title, a URL, a brief description, and a list of categories under which the resource falls. Other users may comment and vote on posts.

The categories are created by the users, so they can get numerous. To make navigating through categories easier, I implemented a search feature. A user may have to run many searches before finding the category she wants, and I didn't want to make her click a submit button for every search. Instead, I wanted a "live search", which is run automatically every time the text in the corresponding input element is modified.

I liked building this feature in Rails, because the implementation involves all three parts of the Model-View-Controller pattern, as well as some client-side processing. For the most part, I followed the implementation suggested by Ryan Bates in his Railcasts series. In this post, I will describe the model side of that implementation and comment on a couple of details of ActiveRecord queries.

Model:


On the model side, we need a method on the Category class that will do the actual searching. Here it is. I want to comment on two aspects of this code. The first, not immediately apparent (to me), is that it doesn't actually return an array of Category objects. ActiveRecord query methods, like where and all, generate SQL queries but don't run them directly. Instead, they return an ActiveRecord::Relation object. It is only when a kicker method like each is called on the ActiveRecord::Relation that the records are loaded from the database into memory. This approach, called lazy loading, conserves memory by not loading records until the instant they are needed. It also improves performance by pushing tasks down to the database, which is faster than the Ruby interpreter at things like sorting and summing. For example, generates the following SQL query This is faster than pre-loading all the records and then sorting them in memory.

If we had wanted the opposite of lazy loading, eager loading, we would have called load on the ActiveRecord::Relation returned by where and all. The records would then have been retrieved and stored in an array instance variable on the ActiveRecord::Relation.

The second point I wanted to comment on is illustrated on line 11. That code would also have worked if we had constructed a SQL statement directly and passed it to where However, this is a bad idea, because we would be passing unsanitized user input to the database, leaving it vulnerable to a cross-script injection attack.

In the following posts, I will tackle the controller, view, and client-side parts of my live search implementation.

Saturday, August 2, 2014

Twitter ERD

I was curious if there are tools that can automatically generate entity relation diagrams (ERDs). I found three (schemacrawler, rails-erd, and railroady). In general, however, it doesn't seem like that many people are interested.

In any case, I applied these tools to the mock Twitter (https://github.com/misha354/twitter) that my instructor at Tealeaf Academy is building as a live-coding demonstration. Here are the results:

schemacrawler:




I like the visuals, but the main limitation of this tool is in the name. It only maps the database schema and doesn't know about associations defined at the model layer. In fact, it's pretty smart and can infer associations from table and column names (like the association between mentions and users). But if the foreign key column isn't named after the table it references (like in the relationships-users associations), the association is lost on schemacrawler. In addition, not seeing the model layer, schemacrawler obviously doesn't see indirect associations like leader user -> relationship user -> follower user.

rails-erd:

It appears that this gem is no longer maintained. But it's still up on github. Here is its ERD:


rails-erd does see associations at the model layer, so it shows us that relationships points to the users table and that user has two indirect