Archive for June, 2007

Create custom commands in Git

Friday, June 22nd, 2007

Let me explain a bit about Git before I get into the subject of this post. click hear if you don’t want to read my opinion on Git

Git is a wonderful thing. It’s fast, provides a great speed boost, and it’s quick. It does merging like I want an SCM to do.

Git is also a terrible thing (for me). It discards most of the command knowledge I have accumulated with SVN, or even Mercurial (my previous DSCM of choice), and sometimes replaces that knowledge with poorly exposed commands or completely different concepts. An example of the latter would be the git-revert command how it compares to the svn revert command. That isn’t so much of a big deal since Git doesn’t (and shouldn’t) let the decisions of a separate project dictate its interface; that said, it doesn’t change the fact that 95% of Git new adopters will be coming from an environment where revert undoes your currently uncommited changes.

The former is more troubling, but thankfully being worked upon. The analog to svn revert is git-checkout -f. I think that this is mostly confusing when perusing the man page, I thought to myself “Hey, there is git-reset, git-revert, there has to be some other command here like that that undoes my uncommitted changes”. The point is that common operations are not so easily learned in the git workflow. There are some good docs such as Everyday Git, but in general the learning process is somewhat painful for Git. Luckily…

Extending git

Git is made up of a hundred or so tiny applications, that are easily chained together to compose custom functionality. These applications expose functionality at high (end-user) level, and more interestingly at a very low-level. An example would be git-knacks that I hacked together yesterday evening in 20 minutes. They provide an easy way to shelve and unshelve code that isn’t ready for a real commit. More on that later…

What I didn’t know, though, was that the git command uses a simple naming convention for sub commands that you can use to inject functionality into the git command for others t use. git reset executes git-reset and git checkout-index runs git-checkout-index. All you have to do is write your own commands in whatever language you choose, name it properly, and presto… git hello-world is now in your toolchain.

I had thought the git- commands where simply for tab-completion support, and were just aliases into the “git blah” style commands. I was exactly oppositely wrong.

The unix-way got me to switch from Mercurial. Sure, I can write scripts that call to the hg command, but there isn’t a way, that I can see, to get something similar to git-merge-base. You would probably have to hack through and extract out some of the internals of Hg.

link_to as a block helper

Tuesday, June 19th, 2007

When it comes to creating helpers for the view portion of satisfaction, I’ve started taking the approach that once I see something “ugly” twice in the rhtml that it should be extracted out into a helper. In many cases, before that.

Today’s wart? link_to calls that have html for the text content. take for example:

<%= link_to "<strong>#{product.name}</strong><span>#{pluralize(product.topic_count(company), 'topic')}</span>", href, :class => "product_label" %>

ewww… and that doesn’t even get into having href defined above in a <% %> block. So, I extended link_to with the help of alias_method_chain such that it will take a block argument instead of the its normal first parameter.

<% link_to browse_url(product), :class => "product_label" do %>
  <strong><%= product.name %></strong>
  <span>(<%= pluralize(product.topic_count(company), 'topic') %>)</span>
<% end %> 

Much more readable. See the code block_link_to.rb

15 minute hack: Poor man’s function composition

Thursday, June 14th, 2007

I loves me some functional programming and I loves me some Symbol#to_proc (which lets you do .each(&:upcase)).

One of those _fp_ features I wish I had in ruby-land was function composition. Specifically, I find myself writing things like:

initials = ["Foo". "Bar", "Baz"].map{|str| str.first.upcase}

That is fine, but just isn’t as cool as leveraging to_proc in some fashion. In the more “stupid, stupid, stupid” case, i’ve also seen things like:

initials = ["Foo". "Bar", "Baz"].map(&:first).map(&:upcase)  # Eww, unnecessary iterations...

Anways, Ruby does have function composition ([Proc#compose][2]), thanks to the Ruby facets project, but working directly with lambdas isn’t nearly as friendly as I desire. So, I spent 15 minutes to hack together a means that I like. Here’s what it looks like:

["foo", "bar", "baz"].map(&:first >> :upcase)

Which reads, map, using :first then :upcase. >> is left associative, like "foo".first.upcase, opposite of Haskell composition where foobar = foo . bar reads foo follows bar.

Also, the snippet linked below lets you do something like:

  class String
    compose(:initial, &:first >> :upcase)
  end
  "foo".initial   # => "F"

Now a re-implementation of ActiveSupport’s Module#delegate:

class Module
  def delegate(*methods)
    options = methods.pop
    unless options.is_a?(Hash) && to = options[:to]
      raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)."
    end

    methods.each do |method|
      compose(method, &to >> method)
    end
  end
end

Perhaps not necessary, but I found it interesting and fun to write. Thoughts?

compose.rb

Caching time_ago_in_words

Saturday, June 2nd, 2007

I’m a big fan of the “english” timestamps that so often get used in apps these days; “Sat Jun 02 17:55:56 -0700 2007″ means almost nothing to me without active thought. It is much nicer to read “about a minute ago”, it requires no processing on my part. As many of you know, rails bundles this functionality into a helper called time_ago_in_words, and it works wonderfully.

The only issue is that you can’t page cache these timestamps. If you do, “one minute ago” won’t be right in short order. So, I worked up a little remix for time_ago_in_words that solves this problem.

The secret sauce is Javascript, as usual. cacheable_time_ago_in_words outputs a javascript call that does the translation:

  module CachedDateHelper
    def cachable_time_ago_in_words(from)
      js_call = javascript_tag "document.write(time_ago_in_words(#{(from.to_i * 1000).to_json}) + ' ago');"
      <<-EOS
        #{js_call}
        
      EOS
    end
  end

And then, on the javascript side:

  function time_ago_in_words(from) {
   return distance_of_time_in_words(new Date().getTime(), from) 
  }

  function distance_of_time_in_words(to, from) {
    seconds_ago = ((to  - from) / 1000);
    minutes_ago = Math.floor(seconds_ago / 60)

    if(minutes_ago == 0) { return "less than a minute";}
    if(minutes_ago == 1) { return "a minute";}
    if(minutes_ago < 45) { return minutes_ago + " minutes";}
    if(minutes_ago < 90) { return " about 1 hour";}
    hours_ago  = Math.round(minutes_ago / 60);
    if(minutes_ago < 1440) { return "about " + hours_ago + " hours";}
    if(minutes_ago < 2880) { return "1 day";}
    days_ago  = Math.round(minutes_ago / 1440);
    if(minutes_ago < 43200) { return days_ago + " days";}
    if(minutes_ago < 86400) { return "about 1 month";}
    months_ago  = Math.round(minutes_ago / 43200);
    if(minutes_ago < 525960) { return months_ago + " months";}
    if(minutes_ago < 1051920) { return "about 1 year";}
    years_ago  = Math.round(minutes_ago / 525960);
    return "over " + years_ago + " years" 
  }

You'll notice there is no support for the rails-like include_seconds option. That is left as an exercise for the reader (ie. I don't need to use it). Using this helper will let you page cache your views, greatly improving performance. It even has a fallback to absolute timestamps for those out there with a javascript disability.

  • Viagra ordre
  • Cialis en ligne
  • Levitra en ligne
  • Propecia acheter
  • Viagra acheter
  • Acheter cialis
  • Ordre levitra
  • Ordre propecia
  • En ligne viagra
  • Vente cialis
  • Levitra bon marche
  • Propecia en ligne
  • Viagra online
  • Buy cialis
  • Order Levitra
  • Buy propecia
  • Buy viagra
  • Cheap cialis
  • Cheap Levitra
  • propecia online
  • Viagra prescription
  • Cialis online
  • Buy Levitra
  • Order propecia