YUI Loader and asset_host

Posted by yrashk

Are you using YUI Loader with Rails and config.action_controller.asset_host = "http://asset%d.site.com" to distribute your assets? Unhappy with YUI Loader because it has no idea about Rails asset_host? Not anymore!

In your HTML layout:

 
    <script type="text/javascript" charset="utf-8">
        var assetHost = <%= controller.asset_host.to_json %>;
    </script>
 

and in your load procedure:

 
        loader = new YAHOO.util.YUILoader(); 
        loader.base = '/yui/';
        // ...
        if (!assetHost.empty()) {
           loader.assetHosts = [0,1,2,3,4].collect(function (i)  {return assetHost.replace("%d",i); });
        }
        loader._url = function(path) {
         var u = this.base || "", f=this.filter;
         u = u + path;

         if (f) {
             u = u.replace(new RegExp(f.searchExp), f.replaceStr);
         }

         if (loader.assetHosts && loader.assetHosts.length > 0) {
            this.assetCounter = this.assetCounter || 0;
            if (this.assetCounter > this.assetHosts.length - 1) {
               this.assetCounter = 0;
            }
            u = loader.assetHosts[this.assetCounter] + u;
            this.assetCounter++;
     }
         return u;
    };
    loader.require(...);
    //...

Code is somewhat dirty and a kind of monkey patch, but works fine for me so far!

Looking for a partner

Posted by yrashk

Recently I’ve got an idea to implement as an own product, though when I’ve looked around for someone to partner on it I’ve discovered that everybody who I communicate on a regular basis are busy until… well, they really don’t know when they will be free enough to join something. But I’m quite enthusiastic kind of person, and I just can’t wait. I also believe that its very booooooring to be alone on the project (and it’s a kind of competitive disadvantage). So, I’m looking for somebody to partner with me on a product creation.

In short, this product is a combination of an issue tracker and GTD planner, something that I definitely need in my daily work. The main concept is already developed, available upon request.

The project is already at its initial stages of development. I’m trying to make its development as much fun as it is possible (I’m on edge Rails, edge RSpec, integrating RBehave, using Lilu, have no other login other than OpenID [ok, I was just lazy], follow RESTful principle, etc.)

I hope that first version could be launched within a month or so.

I’m looking for a partner that will be able to commit in various activities of a project, from A to Z, from design to marketing.

I realize that it might be silly to look for a partner this way. But one that never tries never gets desired. So let me try? :)

Short QA:

Q: Is it a job offer?
A: Nope. I’d rather offer a very fair amount of equity (something around 1/2 seems to be fair enough?)
Q: What if project will fail?
A: It might happen. Startups are a kind of gamble.
Q: Do you have a business plan?
A: Not really yet.
Q: When this project starts?
A: It has been started few days ago already.
Q: More details on a product?
A: Upon request.

Any other questions/interested to talk? Cool! Comment here or drop me a line to yrashk {at} verbdevdotcom.

P.S. This entry is cross-posted in few variations across several communities, number of friends, etc. I’m going to find out a partner shortly!

ActiveRecord vs. Domain-Specific Method

Posted by yrashk

I had a short discussion on a kind of “philosophic” subject recently. The issue is whether to use ActiveRecord API to manipulate model’s logic, like


@user.friendship.create(:friend => @friend)

or to define simplistic domain-specific methods, like:


@user.add_friend(@friend)
or

@friend.add_to_friends_of(@user)

While first approach has its own benefits (having strict and simple API, just like in RESTful approach), I think that it has two downsides:

1. It’s hardly readable comparing to domain-specific approach. I believe that application’s “language” should be as close to user’s dictionary and semantics. I don’t think users think about adding friends like “oh, I’d like to create friendship with that user!”, it rather sounds “I’d like to add him/her as friend” or something like that.

2. It will work quite fine until you will change your database/application structure; you will need to modify your friendship creation code everywhere. In case of using domain-specific method, you should usually end with just modifying your #add_friend/#add_to_friends_of code.

What do you think about these approaches?

Lilu 0.1.0 released and gemified

Posted by yrashk

I’ve released Lilu 0.1.0.

You can use it as plugin: svn://dev.railsware.com/lilu/lilu/tags/0.1.0 (or svn://dev.railsware.com/lilu/lilu/trunk for the latest version) or just install a ‘lilu’ gem (should be available through RubyForge mirrors soon).

I’m going to work on documentation and improvements soon.

Lilu and Rails

Posted by yrashk

I’ve commited initial Rails support for Lilu. Now you can try to install a plugin (svn://dev.railsware.com/lilu/lilu/trunk) and use the following naming schema:

 app/
   layouts/
      application.html
      application.lilu
   views/
      blog/
       post.html
       post.lilu

Typical application.html should contain complete HTML mockup where some element content should be replaced with actual data:

1
2
3
4
5
6
7
8
9
10
<html>
  <head>
    <title>Hello world</title>
  </head>
  <body>
  <div id="main">
        Body goes here
  </div>
  </body>
</html>

application.lilu is pretty simple:


update('#main').with yield

or even


replace('#main').with yield

post.html could contain either full page with blog post or blog post design itself. in first case, you should prepend your post.lilu with something like use(’#post’).

1
2
3
4
<div id="post" class="post">
  <div id="title">Hello, world</div>
  <div id="text">I'm Lilu!</div>
</div>
1
2
3
4
5
# Uncomment this if you have full page design in your post.html
# use('#post')
update('#post').with :id => @post.id,
                     '#title' => { :id => "title-#{@post.id}", self => @post.title }, 
                     '#text' => { :id => "text-#{@post.id}", self => @post.text }

That’s it, first Lilu + Rails experience. The above code is just from my head (too lazy to check exactly this code right now), so it can contain some minor bugs.

And yes, current Lilu implementation most probably contains bugs and problems. It’s quite young—it’s only few days old. I hope it will be getting better and better each day. I’m working on this.

Some Lilu examples

Posted by yrashk

Before: this and this

I’ve posted some Lilu examples on project’s wiki. It looks pretty nice for me now and it actually seems to be implemented already!

Source code is not well polished yet, some specs are definitely missing yet, couple of thoughts and ideas are around

But anyway it was a short path from idea to something working.

Ongoing improvements in Caches.rb

Posted by yrashk

I’ve spent few hours improving Caches.rb recently. While it is not still released (i.e. no official gem, only svn trunk), I’d like to disclose recent changes and share with my plans about it.

So, what’s done?

  • First of all, it was greatly refactored. The most significant change is that storages are not responsible for caching algorithms anymore. Each storage has only few (like 5) simple API methods that needs to be defined. You can look into the sources (svn://verbdev.com/rubylibs/caches.rb/trunk) to see how they are designed now. Please be aware that API method names are most likely to be changed very soon. I don’t like current naming scheme.
  • One more caching module added—Caches::Storage::Global, which stores cache in a global variable cache.
  • Then, I got rid of CachesConfig. If you need to specify a storage for your instance, just do instance_cache_storage StorageModule. CachesStorage::Instance is used by default (which stores cache in an object’s instance)
  • Caching static methods was requested by caches.rb users several times, so here you are:
 
 class SomeClass
   def self.static_method
    ...
   end
  class_caches :static_method
 end
 
You can also specify storage for static methods: class_cache_storage StorageModule
  • Since I was adding some Rails-specific behavior recently I’ve got some feedback on Rails-specific usage of Caches.rb. Apparently, if you cache static methods in your rails app and you’re running in development mode, your class is going to be reloaded by Rails and hence you will lose your changes. That’s why Caches::Storage::Global has appeared. So, now you can use the following pattern:
 
 class MyModel < ActiveRecord::Base
   def self.static_method
    ...
   end
  class_cache_storage Caches::Storage::Global 
  class_caches :static_method
 end
 
  • I’ve added experimental method caches? which should be used to check class whether it caches this or that method. I haven’t decided whether it makes any sense, so may be I will drop it.

Seems that it’s all.

Now, few words on my plans. I plan to:

  • perform a code cleanup, refactor it to the extent possible
  • invent a better naming schema for storage API (though I’m still not very satisfied with it)
  • improve specs, perform more tests
  • release new version
  • think about adding more sophisticated storage modules, like support for memcached.

Update: I’ve performed some refactoring and added memcached support (Caches::Storage::MemCached). Everything is available in svn trunk for testing purposes. I plan to release new official version (0.4.0) soon.

Thinking in Functional Ruby 6

Posted by yrashk

This article is a result of yesterday’s code review session I have done.

I have found that I don’t like the following code (well, a code like this):

def topmost_matches(str,limit=3)
  matches = []
  str.each(' ') do |word|
    word = word.strip.downcase
    matches << word if Entity.find_by_name(word)
  end
  return matches[0, limit] if matches.size > limit
  matches
end

Here is what we can do with this code to make it better:

Well, first of all, let’s simplify last two lines. Since if even matches.size is less than limit, matches[0,limit] will return the whole matches array. This means that we don’t need any kind of explicit condition:

  matches[0,limit]

Then, I really can’t say that the rest of the code is easy to read and understand. It is not readable, it is rather interpretable. And here we can use some Ruby power to do this stuff meach easier and nicer:

 str.split(/\s+/).         # here we split str by whitespace characters
      collect(&:downcase). # then we downcase each word
      select{|word| Entity.find_by_name(word)}[0,limit] 
                           # ...and finally we select that words
                           # that are found within Entities names

Also, it is pretty easy to add even a more complex functionality to this single functional “query”. For example, we also may want to sort results according Entities ratings, descending. No problem!

 str.split(/\s+/). 
      collect(&:downcase).
      select{|word| Entity.find_by_name(word)}.
      collect{|word| Entity.find_by_name(word)}. # collect respective Entities
      sort_by(&:rating).reverse.                 # sort them by rating, descending
      collect(&:name)[0,limit]                   # collect their names, and return topmost 
                                                 # of them

Of course, the above code is still a heaven for code reviewer, but it is out of this article’s scope.

Update: According to improvements proposed in comments, it could be even that easy:

 str.split(/\s+/). 
      collect(&:downcase).
      collect{|word| Entity.find_by_name(word)}.compact.
      sort_by(&:rating).reverse.               
      collect(&:name).first(limit)

Continuous Integration: Cerberus 13

Posted by yrashk

Jordan Arentsen said that he would like to read about Continuous Integration. With a great pleasure!

I would like to talk about Cerberus tool and how we apply it in particular. Cerberus is a great simple tool to perform rake tasks continuously on your project whenever it updates.

I will guide you through the setup.

  • First of all, make sure that your subversion client is at least at version 1.2. It will not work with earlier versions.
  • Setup an email alias for notifications, something like dev-foobar@foobar.com that will forward any email (except spam!) to your team.
  • Then, simply install Cerberus from a gem: gem install cerberus
  • Create a user for cerberus: adduser -m cerberus, passwd cerberus
  • Log in as a cerberus (or just use sudo -H -u cerberus as a prefix)
  • Assuming you have a project at file:///var/lib/svn/foobar/foobar/trunk just do
 
cerberus add file:///var/lib/svn/foobar/foobar/trunk  \
               APPLICATION_NAME=FooBar \
              RECIPIENTS=dev-foobar@foobar.com
 
  • Open /home/cerberus/.cerberus/config.yml and specify there such parameters:
 
publisher:
#  active: mail jabber rss campfire irc
  active: mail campfire # you may also use irc, jabber and rss,
                                     # which we are not using at the moment
  mail:
    sender: cerberus@foobar.com
    address: foobar.com
    port: 25
    domain: foobar.com
    #authentication: plain
    #user_name: someuser
    #password: somepassword
  campfire:
    url: http://cerberus@foobar.com:crbrs@foobar.campfirenow.com/room/58153
#  jabber:
#    jid: cerberus@gtalk.google.com
#    port: 5222
#    password: mypass
#    digest: false
#  irc:
#    nick: cerb
#    server: irc.freenode.net
#    channel: cerberus
#  rss:
#    file: /usr/www/rss.xml
builder:
  rake:
    task: db:migrate # we are also adding here 'spec spec_with_rcov ', since we use
                                # both RSpec and RCov
 
* Then, open /home/cerberus/.cerberus/config/FooBar.yml and edit it:
 
---
publisher:
  mail:
      recipients: dev-foobar@foobar.com
  # specify the rest publishers per project parameters here
scm: 
  type: svn
  url: file:///var/lib/svn/foobar/foobar/trunk
changeset_url: http://trac.foobar.com/changeset/  # If you have Trac (you should! :), integrate with it                                                                                  
 
  • You can override system-wide settings (/home/cerberus/.cerberus/config.yml) with project-wide settings (/home/cerberus/.cerberus/config/FooBar.yml), (I’d suggest to pay attention to at least builder: => rake:) (consider looking at a full list)
  • You can perform a test by typing cerberus build FooBar. It will email you about project setup.
* Now, add it to crontab to run it as frequently as you wish. It does not consumes much time if there was no fresh checkins in a repository, it only runs tests against your code if your repository was updated. We use to run Cerberus each 10 minutes:
 
*/10 * * * * cerberus buildall

  • You’re done!

Few more tips:

  • If you want Cerberus to rebuild FooBar from the scratch, simply rm -rf /home/cerberus/.cerberus/work/FooBar
  • I strongly recommend to generate RCov diagram each time Cerberus runs. Here is how you can integrate RCov with RSpec:

lib/tasks/rcov.task:


require 'rake'
require 'spec/rake/spectask'

Spec::Rake::SpecTask.new('spec_with_rcov') do |t|
    t.rcov = true
 end

  • To integrate it without RSpec, look here (not sure it is what you need exactly, however)
  • Then simply symlink generated coverage directory to some web-accessible (preferably httpauth protected)

I will try to talk more on a coverage analysis later. Now it’s too late (or too early, 4:45 AM)

Few Tips About How to Become a More Effective Developer

Posted by yrashk

Today I want to talk a bit about a topic that seemed to be quite trivial for me and people around, but I’ve found that some things that are trivial for us, aren’t obvious for everyone. So I want to share my thoughts about how to become a more effective developer. I do not pretend to be a guru in this area, it is just my own experience.

Planning

Planning is the first thing to think about if you want to become effective. No need to perform a prioritization or long-term planning. What you actually may need is the following path:

  • write down a list of tasks that you need to accomplish
  • pop those that could be completed in a matter of minutes and put them into ‘RightNow’ folder
  • then sort each task according to the milestone it relates to. If a task should be completed by tomorrow’s morning, then put it to ‘Today’ folder. If it should be done by the end of week, put it into ‘ThisWeek’ folder. If it should be done by the end of a month, put it to ‘ThisMonth’ folder and so on.

Simple enough. Few advices on tasks:

  • define them shortly and cleanly
  • be realistic on time consumation estimations

Execution

So, when you have a plan, you need to work properly with it. It is pretty easy to do:

  • first look into RightNow folder. If there is something to do, do it now. If it is empty, proceed to Today. And so on.
  • the rule of thumb is that you shoud not proceed with a next folder until your current folder is empty.
  • of course, you may need re-plan your tasks if some circumstances were changed. Pay attention to keep your plans up-to-date, that’s vital.

Source Code Management

Source code is the most important result of you work, so you need to manage it properly, aren’t you?

  • Use version control management system. It could be Subversion, Darcs, Git, whatever you like the most (Subversion is quite popular among Rails developers, and I will use it here for examples)
  • Check in your changes frequently. Why? First reason is that you will most likely avoid breaking own code. Once you’ve done task, check it in. Another reason is that if you’re working within a team, they surely would like to have all the code integrated as earlier as it is possible.
svn ci -m "Ticket #2: Improving code quality"
  • Keep your checkin messages descriptive. “another change”, “fixed bug” aren’t descriptive at all.

Example:

svn ci -m "Workaround for a defect #267"
  • Install something like a Trac. It will allow you to track your code changes and issues. It will simplify your life greatly.
  • If you’re working within a team, consider updating your repository (svn up) each few hours and reading others’ changesets at least once a day (in a morning). It will keep you up-to-date and you will not trap into a need to resolve a lot of source code conflicts.
  • Once you’ve trapped into a source code conflict (Subversion reports you about it with a ‘C’ tag for a file), resolve it ASAP. Merge changes outlined in a conflict file and then say svn resolve path/to/file

Source Code

Now, about source code itself.

  • Be lazy. Type less. If you are a happy TextMate user, press esc frequently to autocomplete your text. It will help you to avoid mispelled variable/method names.
  • Follow naming conventions. This is particulary easy with Rails, because it has already established common naming conventions. Ruby itself offers some conventions as well, like appending boolean methods with ?. Following naming conventions will simplify your life greatly, believe me.
  • Use descriptive names for your variables, methods, symbols, etc.
  • Write a code that should be readable, not interpretable. Anybody should be able to understand what your code does by just reading it as a subset of English with some mix with programming language constructs. If you need to interpret code in your brain to understand what it is doing, then it’s potentially a bad code. This is quite hard rule to follow, and it should not be applied to every and every bit of your code. But it’s important to not underestimate its value.
  • Shrink your code size. Avoid extra meaningless constructs. Use short forms of them.
Example: do_something if reasonable? instead of

if reasonable?
   do_something
end

  • Review your code before a checkin. If it smells bad even a bit, try to improve it.
  • Ask your colleague to spend 15 minutes reviewing your code.

Tests

It’s a large topic I will surely cover in some of my future. Let me outline few things:

  • Write test strictly before any implementation code. It will a) guarantee you’re implementing what you need exactly b) speed up your actual development significantly
  • Consider using RSpec. It allows you to write tests in a way that is much closer to English and it will also allow you to group your tests with ease:

context "Newly created Issue" do

 setup do
   @issue = Issue.new
 end

 specify "should have at least 2 questions" do
   @issue.should_have_at_least(2).questions
 end
end

  • Make sure your tests (or specifications in a RSpec terminology) are passing before each repository checkin.
  • Setup a continuous integration software like Cerberus. It will alert you if code becomes broken accidentally.
  • Consider generating RCov code coverage diagrams on a regular basis (let Cerberus do this!). This way you will be sure that your code is well covered by tests. Of course, RCov has to say nothing about how qualitative your tests coverage is, but it could say you whether you’re running each piece of your code by your tests or not. This is quite important.

TO BE CONTINUED: I would like to talk more on some tips, like Rails-specific ones, in the future posts. I’m going to add more details on an issues I’ve mentioned above, supply with more details. Stay tuned!

TO BE UPDATED: If I will find that I forgot to mention something important here, I will update this record.

P.S. Please advise me – may be the above issues are obvious for everyone and I should not write about them and save your and my time? :) I’m just not sure whether it have sense. If it has – then great!

We Are Hiring Internationally 0

Posted by yrashk

I’m happy to announce that Railsware is starting hiring internationally. We are obviously not requiring to relocate, we are just comfortable with telecommuting.

We’re building a networked team of decent Ruby on Rails developers. We’re looking for you.

We are fans of Rails. We like smart code. We like smart people. We like agile processes. We prefer to be an an edge.

If you’re interested to join a team of like-minded persons, then contact us at job@railsware.com. We are waiting for you!


Railsware is headquartered in Kiev, Ukraine, one of the top world outsourcing destinations.

Micro-management, Naming Conventions and Rails 0

Posted by yrashk

Today I’ve heard yet another story about that bad project manager who was enjoying micro-management in an aspect of naming conventions. He was requiring to start interface names with I letter, he was pushing some database rules for fields naming, foreign keys naming, etc.

And developers didn’t like what he do. I think it is mostly because each developer has his own style, preferences, sensation of reasonabe and beautiful. Common code style is rather good, but there is probably some kind of competition between technically-savvy managers and developers.

But what did Rails offered to both project managers and developers? Common naming conventions. Reasonable naming conventions that simplify life, if you use them. If you don’t use them, you should do something that is not directly related to a problem you should focus on. Now project manager could save his/her time and nerves and developers could stop competing with him/her. It’s a win-win situation, I suppose.


One of my friends asked me yesterday via IM: “What if I want to let Rails path to controllers be CamelCase?” I said “What is the reason for you to want it?” He said “BecauseCustomerWantsSo”.

Most of developers are opposing micro-management, and they have reasons. But I’m rather against blindly opposing micro-management. It’s important to realize that micro-management efforts could differ.

My daily activities in Railsware include technical leadership tasks, just like code review. I review every important commit done, at least briefly. And I supply our developers with tasks/advices about how they should improve their code to make it better, easily readable, manageable, testable, etc. While sometimes I could reasonably expect counteraction to such kind of management, I experience it quite rarely. I strongly believe that this kind of “mentorship” leads us to a better quality. As far as I can understand, this way our developers could benefit by a) resulting in a cleaner and better own code b) learning some patterns they didn’t knew before c) having less time spent on testing and debugging.

Actually, what’s bad with micro-management? I have a supposition that the most bad thing with it is that manager is using a power of his/her position to press on subordinates. This sounds reasonable, at least for me. I should take it into account. I’m going to introduce something like cross-developer/cross-project code review sessions within our team.

Happy hacking!

object_id_session 0

Posted by yrashk

I’ve developed a very simple plugin that allows to store ActiveRecord models over session using their IDs transparently.

So, for example, you are saving User in a session:

 
                session[:user] = User.find(1)
                session[:newuser] = User.new
 

After this, your session will look like this:

This plugin does not requires you to change your code. To get your object back, you should just ask session for it:

 
                session[:user]
 

and you’ll get User instance. object_id_session is responsible for conversions to be performed.

It is a very early version, its code isn’t perfect and there are probably some bugs, but it seems to pass some basic tests (which are written in RSpec, of course)

You can install this plugin from svn://verbdev.com/rubylibs/object_id_session/trunk

Why it could be useful? Well, I’ve just thought that it is quite stupid to pass big objects around session all the time, checking whether they are still available in the database each time, etc.

May be I’m just missing something and this kind of functionality is already implemented somewhere. In this case, just kill me, yeah! :)

RSpec on Rails: acts_as_authenticated 1

Posted by yrashk

As you may know, we at Railsware are using RSpec now.

I’ve found that I need ospecifications for a acts_as_authenticated plugin.

While I was able to find a spec for its controller at caboo.se, I haven’t found any spec for a model. May be I’m just blind this night, but anyway I’ve wrote my own

Update: I’ve found Caboo.se implementation of specification being far from perfect, so I have improved it a bit: account_controller_spec.rb

Update:Jonathan used this blog entry to deliver RESTful authentication specifications Nice to hear!