tag:blogger.com,1999:blog-25187854374986247512024-03-13T18:06:35.233-05:00Artistic CodingMehttp://www.blogger.com/profile/05577722747266320930noreply@blogger.comBlogger2125tag:blogger.com,1999:blog-2518785437498624751.post-1110169940172212942006-11-21T17:45:00.000-05:002006-11-21T18:15:01.756-05:00The Danger Of Best PracticesEvery person has at some point experienced the phenomenon known as writers block. For programmers, this manifests as a block against writing code or getting into the flow of programming. Like all creative enterprises, this block often stems from a fear of creating. Our creative cogs get stopped up because we fear that what we create might not be good enough, correct, or otherwise valid.<br /><br />In programming, this fear often manifests as "am I doing this the right way?" I sometimes have trouble getting into the flow of coding because I'm constantly thinking "Maybe I'm doing this wrong. Maybe there's a better way. Maybe maybe maybe." This fear can be debilitating and is a would be creator's biggest enemy. Unfortunately this problem can be compounded in the coding world by the existence of Best Practices.<br /><br />Best Practices, which describe the "best" way to accomplish a given coding task, are usually useful and helpful to a programmer. The problem occurs when one begins to fear ever violating some ethereal Best Practice. It isn't a specific best practice which causes the problem, but the existence of Best Practices as a whole. Letting the fear of not doing something the "best" way restrict your creative flow is utterly pointless and self-defeating. The quest for best practices should be a relaxed one of new learning, not a frantic one of self-validation.<br /><br />The fix to the problem is the same as it is in all creative ventures - damn the torpedos and full speed ahead. Ignore other concerns, other ways of doing things, other "best practices" and just do it. Write that program. Code that library. Going back and changing things later is easy. If you never write anything, you'll never have anything to change. Apply Best Practices after the fact. Refactor. Revise. If you happen to see a better way of doing something in the future, modify your source, run your tests, and check in a new revision. Never let "what could be" get in the way of "what can be right now."Mehttp://www.blogger.com/profile/05577722747266320930noreply@blogger.com0tag:blogger.com,1999:blog-2518785437498624751.post-16048677124162251012006-11-16T07:37:00.000-05:002006-11-16T10:09:45.584-05:00The Rails Path: Tracks (Part 1)The amazing <a href="http://weblog.jamisbuck.org/">Jamis Buck</a> has teamed up with <a href="http://www.koziarski.net/">Michael Koziarski</a> to offer a new coding series called <a href="http://www.therailsway.com/">The Rails Way</a>. Some of the comments in their <a href="http://www.therailsway.com/2006/11/15/tracks-part-1">first installment</a> mention the fact that some people new to rails may not have a very strong grounding in Ruby. In order to help those people understand the Rails Way wisdom, I will be explaining parts of their post in the context of a basic beginner to Ruby/Rails.<br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">Acts_as_name_part_finder<br /></span></span><span style="color: rgb(102, 102, 102);font-size:85%;" >(original code <a href="http://dev.rousette.org.uk/browser/trunk/tracks/lib/acts_as_namepart_finder.rb?rev=333">here</a>)</span><br /><span style="font-size:100%;">The acts_acts_as_name_part_finder macro adds a method called find_by_namepart that allows for one to search by an entire name or a piece of a name. It would be used like this:<br /><textarea name="code" class="ruby" cols="60" rows="10"><br />class Project < ActiveRecord::Base<br /> acts_as_namepart_finder<br />end<br /></textarea><br />You would then be able to do things like:<br /><textarea name="code" class="ruby" cols="60" rows="10"><br />Project.create(:name => "Happy");<br />Project.create(:name => "Super Project");<br /><br />Project.find_by_namepart("Happy"); # <project name="Happy"><br />Project.find_by_namepart("Super"); # <project name="Super Project"><br /></textarea><br />The point the Rails Way is making is that it is unnecessary to declare an entirely new acts_as_* macro just for the sake of adding a single function. An additional point is that this function is only ever used in one model. The lesson here is don't abstract code into a new macro/file if its only being used in one place. Wait until you see yourself copying and pasting the code into other places before you start abstracting it.<br /><br /><br /></span><span style="font-weight: bold;"><span style="font-size:130%;">Modules<br /></span></span>If you're learning Ruby/Rails, you really should know what modules are and how to use them. For those that need a quick refresher, a module is just a container around some pieces of code. Usually you define methods in a module and then use the "include" keyword to add those methods/code into your class. For example:<br /><textarea name="code" class="ruby" cols="60" rows="10"><br />module MyModule<br /> def say_hi<br /> puts "hello"<br /> end<br />end<br /><br />class MyClass<br /> include MyModule<br />end<br /><br />MyClass.new.say_hi # => "hello"<br /><br />class MySuperClass<br /> extend MyModule # adds the methods in MyModule as class methods<br />end<br /><br />MySuperClass.say_hi # => "hello"<br /><br /></textarea><br />If find_by_namepart had been used in multiple places, it would have made sense to define the function inside a module and then use "include" in the places where you need that functionality.<br /><br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">Acts_as_todo_container<br /></span></span><span style="color: rgb(102, 102, 102);font-size:85%;" >(original code <a href="http://dev.rousette.org.uk/browser/trunk/tracks/lib/acts_as_todo_container.rb?rev=333">here</a>)</span><br />The first point the Rails Way makes is that a lot of the original code for the acts_as_todo_container was solely for the purpose of having the database automatically include associated 'has_many' models in a single db query. This allows for:<br /><textarea name="code" class="ruby" cols="60" rows="10"><br />class Context < dependent =""> true, :order => "completed_at DESC"<br /> acts_as_todo_container :find_todos_include => :project<br /></textarea><br />With this code, if I made a call to Context.find_not_done_todos I would get back a list of Todo's as well as their associated Projects. A great majority of the actual acts_as_todo_container deals with adding in this :find_todos_include functionality. By using the :include option in the has_many association, all of this code can be eliminated and the resulting code simplified to the point where a separate acts_as_todo_container is not necessary. Instead, the Todo handling code can be placed into a basic module and be "included" as normal.<br /><br /><span style="font-weight: bold;">Other Simplifications<br /><span style="font-weight: bold;"></span></span>Besides removing :find_todos_include, the Rails Way introduced a number of syntactical simplifications:<br /><br /><span style="font-size:130%;"><span style="font-weight: bold;">1. </span></span>Replacing code like:<br /><textarea name="code" class="ruby" cols="60" rows="10"><br /># original<br />def not_done_todos(opts={})<br /> @not_done_todos = self.find_not_done_todos(opts) if @not_done_todos == nil<br /> @not_done_todos<br />end<br /><br />#after<br />def not_done_todos<br /> @not_done_todos ||= find_not_done_todos<br />end<br /></textarea><br />The ||= operator checks to see if @not_done_todos is nil. If it is, it sets it to find_not_done_todos. Otherwise it just evaluates (and returns) @not_done_todos. This is exactly what the previous code did in a more succinct form.<br /><br /><span style="font-size:130%;"><span style="font-weight: bold;">2. </span></span>Replacing code like:<br /><textarea name="code" class="ruby" cols="60" rows="10"><br /># original<br />def find_not_done_todos<br /> todos = Todo.find(:all,<br /> :conditions => ["todos.#{self.class.base_class.name.singularize.downcase}_id = ? and todos.type = ? and todos.done = ?", id, "Immediate", false],<br /> :order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",<br /> :include => find_todos_include)<br /><br />end<br /><br /># after<br />def find_not_done_todos<br /> self.todos.find(:all,<br /> :conditions =><br /> ["todos.type = ? and todos.done = ?", id, "Immediate", false],<br /> :order => <br /> "todos.due IS NULL, todos.due ASC, todos.created_at ASC")<br /><br />end<br /></textarea><br /><span style="font-size:100%;">Instead of making a call to Todo.find and trying to manually simulate the has_many relationship through use of statements like "#{self.class.base_class.name.singularize.downcase}_id", they use the has_many association defined in the model to make the simple "self.todos.find" call. This is again a result of moving the :include parameter to the has_many association and allowing the built in rails macros to handle all the complex stuff.</span><br /><br />That's everything for this post. If anyone has any questions (no matter how basic) please feel free to ask. To make things clear, I am not trying to steal the Rails Ways' thunder. My goal is to elaborate on their points so that people who don't have much of a Ruby/Rails background can still understand what's going on.<br /><span style="font-size:100%;"></span>Mehttp://www.blogger.com/profile/05577722747266320930noreply@blogger.com58