The Learning Curve of Ruby on Rails


One major reason why I ended up picking Ruby on Rails as my next major framework to learn was how people have stated that building applications is pretty quick. Although there are frameworks for both Perl and PHP that are similar to Rails, I ended up doing a massive switch over in languages both as a personal challenge and the fact that I’m somewhat sick of Perl and PHP.

Already, I’ve found that Rails by itself has a fairly large learning curve. Most examples can you up and running quickly. However, anything beyond your typical CRUD “Hello World” application is going to require a lot of trial and error. Triple all the difficulties if you never have or barely touched Ruby, which in itself has its own problems as well as adapting to CoffeeScript and SASS for CSS.

One of the more interesting features that has been causing me both frustration and delight simultaneously is the scaffolding feature. Scaffolding essentially creates a lot of basic CRUD boilerplate type of code. This is a neat feature…if you have a pretty good idea of what you want to build ahead of time. However, this can get sticky quickly once you start customizing the boilerplate code and expanding out your models. There are ways to handle this scenario such as destroying the scaffold and/or using migrations. Unfortunately, I think by destroying the scaffold, you’ll lose your customized code in the process.

So what I think ends up happening (at least from a n00bz point of view) is that most people end up using migrations. Migrations, just like scaffolds, are both a blessing and a major pain in the ass. The concept of a migration is that you allow the framework for the most part to manage your database. This is a GREAT THING. I really love this aspect because it pretty much standardizes the implementation for dealing with your database. All too often I’ve encountered sloppily implemented homebrewed schemes for handling database changes. In turn, there’s no standard practice that people can easily turn to outside of the owner for these changes (and/or the DBA). With Rails’ method of migrations, you have a methodology for handling all changes and you get the community, documentation and support all rolled into one framework.

That said, the migrations methodology that Rails provides can be annoying. Now, I haven’t gone that far into migrations, but my experience so far has demonstrated to me that Rails can get sloppy quite quickly. For one, there’s a directory that contains all the migrations. I can easily fathom hundreds of migrations for complex applications in that directory. Also, there’s the naming of migrations. Part of the naming convention makes sense and actually can be helpful since the naming convention is somewhat descriptive. However, something I’m wondering is how one ends up dealing with constant change to a model/table over time. It feels as though you could potentially end up with a ton of redundant and/or misleading names.

Lastly, the migrations themselves are all timestamped. While the intent is nice, what really Rails needs to implement is a local version control system for migrations. It feels as though working off a single model and evolving it constantly would be better handled through version control as opposed to the timestamp system. I heavily dislike the idea of constantly writing and naming new migrations for any additional changes I need to make to my schema. By doing this along with having the version control system, perhaps Rails could intelligently work with your models and views to update the necessary attributes rather than you having to manually go through and editing them after you generate your scaffolds. In addition, you could have cleaner versions of these changes being propagated to the real central version control system.

Moving along, the next part I had a bit of stumbling on is the notion of attr_accessor vs attr_accessible. I wanted to create an attribute on one of my models that was not linked to the database. In addition, I wanted this particular attribute to have its own default values (gee a model that isn’t just a fucking clone of a row from a database? Who would’ve thunk?) Believe it or not, this situation isn’t as easy as it sounds, at least from finding reasonable documentation on the subject.

So part of the problem is differentiating between attr_accessor vs attr_accessible. attr_accessor essentially creates your getter/setter methods for an attribute in your model. on the other hand, attr_accessible imposes some security on what can be sent to the constructor when creating a new instance. The example given that I found was something like a password or social security number where you might not want to just send blind data in. But that’s where my confusion comes in because apparently you can still access your attributes normally when you use the attr_accessible method. So I’m still trying to figure out the point of this. The only logical thing that came to mind was that you could create non-column based attributes by utilizing the attr_accessor method. An example is a password confirmation field. There, you do not need to persist the confirmation but you still need to validate that it exist and matches the password from a form.

Well, great. So why did I need something like this in the first place? In my application I have a large number of attributes that are pretty much clones of each other. The display logic pretty much is repetitive so I had a choice of either hard/hand coding each attribute or attempt to automate the generation of these attributes in my form. I wanted the model to have the knowledge of which attributes would be required here just in case I needed to use this logic elsewhere in the future. In turn, I would define these attributes in an array attribute that I could iterate over in my form. Sounds easy? Not exactly with Rails.

First problem: where do you initialize this information? After doing numerous google searches, the common denominator ended up being in the after_initialize method. I tried using this methodology where I would do something like:

def after_initialize
    self.default_stats = ['age', 'strength', 'intelligence', 'wisdom', 'willpower', 'dexterity', 'personality', 'reaction', 'constitution', 'comeliness', 'leadership', 'perception']
end

However, when I attempted to access my default_stats attribute in my view, I kept getting a “Method Not Found” error. This occurred during an edit action where my controller would instantiate the related object and pass it back to the view. I have no idea why my array wasn’t properly getting initialized. So for now, I ended up cheesing this a bit by changing that function into something called “stats”, calling it in the view and assigning it to a variable to iterate over like this:

  <% stats = @race.stats%>
  <% stats.each do |stat| %>
  <div>
      <%= f.label :name, stat%>
      <%= f.text_field stat, :size => 2, :maxlength => 2%>
  </div>      
  <% end%>

This code would almost work except for one issue: stat. Here “stat” ends up throwing a “method not found” error in the f.text_field statement. Actually, what goes on is that the stat variable ends up becoming interpolated into something like “age” or “intelligence” and those in turn end up receiving the “method not found” error. Why is that?

It’s because our friend attr_accessor requires these attributes as well. Wait what? The fields from my default_stats attribute are actual column names that I’ve already defined in the attr_accessible method for that particular class. I think because I added them late and that they did not have any value, they ended up just disappearing off the face of this map and can’t be loaded for whatever reason. So to compensate, I ended up just adding them to the attr_accessor method and the view ended up working.

I’m probably bungling a lot of ideas up but certain aspects of Rails just haven’t clicked for me yet. I suppose I could’ve just put them into a local array in the view instead. But it just felt wrong. I want my views to be dumb about certain aspects that only my models should know about. I’ll probably re-use this mechanism in a different area. I shouldn’t have to search through my views to pull out logic that really belongs in the model.

Yet this example illustrates a few frustrations some simple, possibly common things you would find in many frameworks that are not adequately explained upfront. Again, it feels like my inexperience with Rails might be de-railing me (nyuk nyuk) but I wish things like this were more apparent. Wouldn’t it just be easier to have all your attributes lined up with the ability to define them in a single shot without going through all these other odd mechanisms? I do feel as though I’m jumping through a few unnecessary hoops to accomplish something most OOP structures have as a default.

Lastly, I dabbled a bit with the unit testing aspect. This was one piece that sold me on wanting to really learn Rails. I noticed that Rails has several different levels with testing, including both unit and integration testing. Also, just in browsing through the directories, I can see performance testing as something I can eventually delve into. Setting up the tests with Rails is fairly easy as Rails pretty much generates all the necessary files in the right places to get you started. But you’re pretty much on your own from that point on. As of now, I’ve only just tested a simple model by writing test around the validation aspect. When I ran the test, a plethora of output poured out and I had to scroll around to find out exactly what went wrong. The issue wasn’t very descriptive and I’m certain I’ll need to figure out how to improve upon making the messaging far more meaningful.

I felt slightly disappointed thus far with this. I know there’s various books/sites dedicated to test driven development using Rails as well as integrating them with Agile methodologies. While the resources are available, I kinda was hoping that the framework would force me by default to get into the habit of writing unit tests. Instead, once again I see the unit test aspect psychologically as just necessary busy work that can be indefinitely delayed.

Sure, enforcing unit testing on day 1 might be overwhelming for people who want to adapt Rails. The idea reminds me of how my old programming professor from UCI once told us how Java was a terrible teaching language because of all the various concepts you’re forced to teach on day 1. But I think that getting into good habits early is better than forcing yourself to change later on. In short, it feels like a missed opportunity for the framework.

That said, I haven’t gone very far yet with Rails. I created a pretty decent project for myself just to ram my head against the various cases of developing a more complex application. I’m hoping that over time as I improve these issues slowly minimize and that my application can be built faster. Also, I would love to deal with more esoteric aspects of the framework so that I can really learn the boundaries of what’s possible.

With all that in mind, one thing that I think about is how one could possibly utilize these concepts with other frameworks. For instance, there’s Zend for PHP. Zend is quite good in that it’s mature. But it’s really piecemeal when it comes to the design since it obeys the “loosely coupled” notion of OOP. I think that things like Migrations would be an interesting piece in the Zend puzzle. I’ve seen some systems like Magento employ a migration-like mechanism. But I feel that there should be a better plugin for Zend that does some of what I mentioned (like the version control aspects)

Despite my complaints, I’m still excited to learn more about Rails. I’m trying to keep an open mind when it comes to learning the in’s and out’s of the framework. I love the fact that it’s one of the more mature frameworks around and has a great deal of support. There’s a lot of really cool technology associated with Rails and I hope that with some dedication and this project, I can learn enough to become a reasonable developer with it.

(Visited 443 times, 1 visits today)

Comments

comments