Named sprintf parameters

I have a question and perhaps a bit of challenge for those with mad
parse skills: Has anyone ever considered named parameters for sprintf/
printf? It would be quite useful (to me at least) to be able to do:

  "I am %(name)s." % { :name => "Tom" ]

Has anyone worked on something like this before? Is there anything
equivalent in the Perl world or other language? I realize we can use
numerals to identify the substitutions, but I feel the labels are much
more readable. Moreover, ultimately it would be interesting to see:

  "I am %(name)s." % binding

Making use of the binding's local_variables.

The challenge, of course, is to override printf/sprintf to do this
(the binding part gets you extra ruby points :wink: Even more
challenging, augment the C code to handle it and submit it as a patch.

T.

class String
   module M
     def % *args
       newstuff
       super
     end
   end
   include M
end

a @ http://codeforpeople.com/

···

On Feb 28, 2008, at 9:57 AM, Trans wrote:

I have a question and perhaps a bit of challenge for those with mad
parse skills: Has anyone ever considered named parameters for sprintf/
printf? It would be quite useful (to me at least) to be able to do:

"I am %(name)s." % { :name => "Tom" ]

Has anyone worked on something like this before? Is there anything
equivalent in the Perl world or other language? I realize we can use
numerals to identify the substitutions, but I feel the labels are much
more readable. Moreover, ultimately it would be interesting to see:

"I am %(name)s." % binding

Making use of the binding's local_variables.

The challenge, of course, is to override printf/sprintf to do this
(the binding part gets you extra ruby points :wink: Even more
challenging, augment the C code to handle it and submit it as a patch.

T.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

That exact feature exists in that form in Python today...
http://docs.python.org/lib/typesseq-strings.html

-T3ch Dude

···

On Feb 28, 11:57 am, Trans <transf...@gmail.com> wrote:

I have a question and perhaps a bit of challenge for those with mad
parse skills: Has anyone ever considered named parameters for sprintf/
printf? It would be quite useful (to me at least) to be able to do:

"I am %(name)s." % { :name => "Tom" ]

Has anyone worked on something like this before? Is there anything
equivalent in the Perl world or other language? I realize we can use
numerals to identify the substitutions, but I feel the labels are much
more readable. Moreover, ultimately it would be interesting to see:

"I am %(name)s." % binding

Making use of the binding's local_variables.

The challenge, of course, is to override printf/sprintf to do this
(the binding part gets you extra ruby points :wink: Even more
challenging, augment the C code to handle it and submit it as a patch.

T.

I released a gem last year (http://jig.rubyforge.com) that defines
a data structure I nicknamed a Jig that enables this sort of thing
either by parsing a string to get the format or by explicit
construction:

j = Jig.new('I am ', :name)
j2 = Jig.parse('I am %{:name:}')

puts j % { :name => 'Tom'} # I am Tom
puts j2 % { :name => 'Alice'} # I am Alice

A Jig is a ordered sequence of objects and 'gaps'. Simple gaps are
identified as symbols during Jig construction but you can also define
gaps that process anything that is used to fill them:

capitalize = Jig::Gap.new(:name) { |x| x.capitalize }
j2 = Jig.new('before: ', :name, ' after: ', capitalize)
puts j2 % {:name => 'bob' } # 'before: bob after: Bob'

In that example you can see that all gaps with the same name
are plugged simultaneously. Unplugged gaps are rendered as
the empty string when Jig#to_s is called.

You can also construct a Jig or fill a gap with a proc/lambda/method
which will be evaluated at the time the Jig is rendered to a string:

    time = Jig.new("The time is ") {Time.now}
    puts time # The time is Thu Feb 28 18:16:07 -0500 2008
    sleep 5
    puts time # The time is Thu Feb 28 18:16:12 -0500 2008

The gem has classes for handling XML, XHTML, and CSS constructs.

I've made a few changes since the initialize release but haven't
gotten around to pushing it a new release to Rubyforge yet....

Gary Wright

http://jig.rubyforge.com

···

On Feb 28, 2008, at 11:57 AM, Trans wrote:

I have a question and perhaps a bit of challenge for those with mad
parse skills: Has anyone ever considered named parameters for sprintf/
printf? It would be quite useful (to me at least) to be able to do:

  "I am %(name)s." % { :name => "Tom" ]

Has anyone worked on something like this before? Is there anything
equivalent in the Perl world or other language?

Hi,

At Fri, 29 Feb 2008 01:57:23 +0900,
Trans wrote in [ruby-talk:292860]:

I have a question and perhaps a bit of challenge for those with mad
parse skills: Has anyone ever considered named parameters for sprintf/
printf? It would be quite useful (to me at least) to be able to do:

Yes, once I had posted the patch for it, and was rejected.

  "I am %(name)s." % { :name => "Tom" ]

Has anyone worked on something like this before? Is there anything
equivalent in the Perl world or other language? I realize we can use
numerals to identify the substitutions, but I feel the labels are much
more readable. Moreover, ultimately it would be interesting to see:

  "I am %(name)s." % binding

Making use of the binding's local_variables.

Though I don't feel it attractive, I believe named parameter is
an important feature for I18N, and IIRC, ruby-gettext has it.

···

--
Nobu Nakada

Can you please explain this? It looks to me like it would allow you to
override a method, but still call the original without using alias.
However, when I try anything like that, it only calls the original
method. The method defined inside of M isn't called. Is that a 1.9
thing or am I misunderstanding what you meant?

···

On Feb 28, 11:29 am, ara howard <ara.t.how...@gmail.com> wrote:

class String
   module M
     def % *args
       newstuff
       super
     end
   end
   include M
end

Hi,

At Fri, 29 Feb 2008 01:57:23 +0900,
Trans wrote in [ruby-talk:292860]:
> I have a question and perhaps a bit of challenge for those with mad
> parse skills: Has anyone ever considered named parameters for sprintf/
> printf? It would be quite useful (to me at least) to be able to do:

Yes, once I had posted the patch for it, and was rejected.

> "I am %(name)s." % { :name => "Tom" ]
>
> Has anyone worked on something like this before? Is there anything
> equivalent in the Perl world or other language? I realize we can use
> numerals to identify the substitutions, but I feel the labels are much
> more readable. Moreover, ultimately it would be interesting to see:
>
> "I am %(name)s." % binding
>
> Making use of the binding's local_variables.

Though I don't feel it attractive, I believe named parameter is
an important feature for I18N, and IIRC, ruby-gettext has it.

You may find the recently announced Jig library of interest. I was sitting
next to Gary during something at RubyConf, maybe RejectConf, and he gave me
a demo. Pretty cool. See Subject: jig 0.1.2 Released - Ruby - Ruby-Forum

Nobu Nakada

--Greg

···

On Fri, Feb 29, 2008 at 01:51:02PM +0900, Nobuyoshi Nakada wrote:

Hi,

At Fri, 29 Feb 2008 01:57:23 +0900,
Trans wrote in [ruby-talk:292860]:

> I have a question and perhaps a bit of challenge for those with mad
> parse skills: Has anyone ever considered named parameters for sprintf/
> printf? It would be quite useful (to me at least) to be able to do:

Yes, once I had posted the patch for it, and was rejected.

  :(

> "I am %(name)s." % { :name => "Tom" ]

> Has anyone worked on something like this before? Is there anything
> equivalent in the Perl world or other language? I realize we can use
> numerals to identify the substitutions, but I feel the labels are much
> more readable. Moreover, ultimately it would be interesting to see:

> "I am %(name)s." % binding

> Making use of the binding's local_variables.

Though I don't feel it attractive, I believe named parameter is
an important feature for I18N, and IIRC, ruby-gettext has it.

Really? It seems like a nice way to apply parameter to templates to
me. Rather then, say,

  xml = %{
    <customer id="#{params[:id]}">
       <name>#{params[:name]}</name>
    </customer>
  }

One could do:

  xml = %{
    <customer id="%(id)u">
       <name>%(name)s</name>
    </customer>
  } % params

A little cleaner --and provides a nice means of reusable
interpolation.

I'll have to look at ruby-gettext.

Thanks,
T.

···

On Feb 28, 11:51 pm, Nobuyoshi Nakada <n...@ruby-lang.org> wrote:

> class String
> module M
> def % *args
> newstuff
> super
> end
> end
> include M
> end
>

However, when I try anything like that, it only calls the original
method. The method defined inside of M isn't called.

...

I also couldn't get Ara's example to work, so I resorted to monkeypatching.
(Yes, I know, I am Destroying Ruby..)

···

On 2/28/08, yermej <yermej@gmail.com> wrote:

On Feb 28, 11:29 am, ara howard <ara.t.how...@gmail.com> wrote:

----------

p "I am %s." % "string"
p "I am %s." % ["array"]
p "I am %s." % { :name => "hash" }
begin
p "I am %(name)s." % { :name => "hash" }
rescue ArgumentError => err
p "caught #{err}"
end

puts "\nMonkeyPatching!\n\n"
class String
  alias :old_percent :%
  def % arg
    arr = (arg.kind_of? Array) ? arg : [arg]
    target = self.gsub(/%\((.+?)\)/){|m|
      raise ArgumentError, "named parameters need hash" unless arg.kind_of? Hash
      arr << arg[$1.to_sym]
      "%#{arr.size}$"
    }
    target.old_percent arr
  end
end

p "I am %s." % "string"
p "I am %s." % ["array"]
p "I am %s." % { :name => "hash" }
p "I am %(name)s." % { :name => "hash" }

p "I am %(name)s %(with)s %(what)s"%{:what => "multiple arguments",
        :with => "including", :name=> 'hash'}
p "I am %(name)s %(with)s %1$p"%{:with => "containing", :name=> 'hash'}
p "I am %(name)s." % [:name, "array"]

-Adam

P.S. It actually looks like patching sprintf wouldn't be too hard, as
long as you disallowed mixing named arguments with any other type.

No you are right. Including a module in a class effectively inserts
the module above the class in the inheritance chain rather than below,
a class can override methods in a module it includes, not the other
way around. I missed Ara's point as well.

Along these lines I was scratching my head the other night when I was
reading Russ Olsen's "Design Patterns in Ruby" and he was showing an
implementation of the Decorator pattern using modules as decorators, I
missed the subtlety that he was using Object#extend to include a
module above the singleton class of a particular object rather than
including it in a 'real' class. But I don't think that technique
applies here.

···

On 2/28/08, yermej <yermej@gmail.com> wrote:

On Feb 28, 11:29 am, ara howard <ara.t.how...@gmail.com> wrote:

> class String
> module M
> def % *args
> newstuff
> super
> end
> end
> include M
> end
>

Can you please explain this? It looks to me like it would allow you to
override a method, but still call the original without using alias.
However, when I try anything like that, it only calls the original
method. The method defined inside of M isn't called. Is that a 1.9
thing or am I misunderstanding what you meant?

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

i was just being stupid. you'd need something like this

cfp2:~ > cat a.rb
class String

   Percent = instance_method '%' unless defined? Percent

   def % *a, &b
     a.flatten!
     string =
       case a.last
         when Hash
           expand a.pop
         else
           self
       end
     if a.empty?
       string
     else
       Percent.bind(string).call(*a, &b)
     end
   end

   def expand! vars = {}
     loop do
       changed = false
       vars.each do |var, value|
         var = var.to_s
         var.gsub! %r/[^a-zA-Z0-9_]/, ''
         [
           %r/\$#{ var }\b/,
           %r/\@#{ var }\b/,
           %r/\$\{\s*#{ var }\s*\}/,
           %r/\@\{\s*#{ var }\s*\}/,
         ].each do |pat|
           changed = gsub! pat, "#{ value }"
         end
       end
       break unless changed
     end
     self
   end

   def expand opts = {}
     dup.expand! opts
   end

end

puts( 'I am @name'.expand(:name => 'Ara') )
puts( 'I am @name' % {:name => 'Ara'} )

cfp2:~ > ruby a.rb
I am Ara

a @ http://codeforpeople.com/

···

On Feb 28, 2008, at 1:09 PM, yermej wrote:

On Feb 28, 11:29 am, ara howard <ara.t.how...@gmail.com> wrote:

class String
  module M
    def % *args
      newstuff
      super
    end
  end
  include M
end

Can you please explain this? It looks to me like it would allow you to
override a method, but still call the original without using alias.
However, when I try anything like that, it only calls the original
method. The method defined inside of M isn't called. Is that a 1.9
thing or am I misunderstanding what you meant?

--
share your knowledge. it's a way to achieve immortality.
h.h. the 14th dalai lama

Looks like you get the prize :wink:

I would like to add this to Facets, though under a slightly different
naming of course. Not sure what though #printfv #sprintfv ?

Thanks!
T.

···

On Feb 28, 3:42 pm, "Adam Shelly" <adam.she...@gmail.com> wrote:

On 2/28/08, yermej <yer...@gmail.com> wrote:

> On Feb 28, 11:29 am, ara howard <ara.t.how...@gmail.com> wrote:

> > class String
> > module M
> > def % *args
> > newstuff
> > super
> > end
> > end
> > include M
> > end

> However, when I try anything like that, it only calls the original
> method. The method defined inside of M isn't called.
...

I also couldn't get Ara's example to work, so I resorted to monkeypatching.
(Yes, I know, I am Destroying Ruby..)

----------

p "I am %s." % "string"
p "I am %s." % ["array"]
p "I am %s." % { :name => "hash" }
begin
p "I am %(name)s." % { :name => "hash" }
rescue ArgumentError => err
p "caught #{err}"
end

puts "\nMonkeyPatching!\n\n"
class String
  alias :old_percent :%
  def % arg
    arr = (arg.kind_of? Array) ? arg : [arg]
    target = self.gsub(/%\((.+?)\)/){|m|
      raise ArgumentError, "named parameters need hash" unless arg.kind_of? Hash
      arr << arg[$1.to_sym]
      "%#{arr.size}$"
    }
    target.old_percent arr
  end
end

p "I am %s." % "string"
p "I am %s." % ["array"]
p "I am %s." % { :name => "hash" }
p "I am %(name)s." % { :name => "hash" }

p "I am %(name)s %(with)s %(what)s"%{:what => "multiple arguments",
        :with => "including", :name=> 'hash'}
p "I am %(name)s %(with)s %1$p"%{:with => "containing", :name=> 'hash'}
p "I am %(name)s." % [:name, "array"]

-Adam

Hmmm... this looks more like an answer to the interpolation question
asked in a earlier thread. Is this a full-fledge implementation? Would
it be a better solution then:

  def String.interpolate(&str)
    eval "%{#{str.call}}", str.binding
  end

T.

···

On Feb 28, 7:13 pm, ara howard <ara.t.how...@gmail.com> wrote:

On Feb 28, 2008, at 1:09 PM, yermej wrote:

> On Feb 28, 11:29 am, ara howard <ara.t.how...@gmail.com> wrote:

>> class String
>> module M
>> def % *args
>> newstuff
>> super
>> end
>> end
>> include M
>> end

> Can you please explain this? It looks to me like it would allow you to
> override a method, but still call the original without using alias.
> However, when I try anything like that, it only calls the original
> method. The method defined inside of M isn't called. Is that a 1.9
> thing or am I misunderstanding what you meant?

i was just being stupid. you'd need something like this

cfp2:~ > cat a.rb
class String

   Percent = instance_method '%' unless defined? Percent

   def % *a, &b
     a.flatten!
     string =
       case a.last
         when Hash
           expand a.pop
         else
           self
       end
     if a.empty?
       string
     else
       Percent.bind(string).call(*a, &b)
     end
   end

   def expand! vars = {}
     loop do
       changed = false
       vars.each do |var, value|
         var = var.to_s
         var.gsub! %r/[^a-zA-Z0-9_]/, ''
         [
           %r/\$#{ var }\b/,
           %r/\@#{ var }\b/,
           %r/\$\{\s*#{ var }\s*\}/,
           %r/\@\{\s*#{ var }\s*\}/,
         ].each do |pat|
           changed = gsub! pat, "#{ value }"
         end
       end
       break unless changed
     end
     self
   end

   def expand opts = {}
     dup.expand! opts
   end

end

puts( 'I am @name'.expand(:name => 'Ara') )
puts( 'I am @name' % {:name => 'Ara'} )

cfp2:~ > ruby a.rb
I am Ara
I am Ara

Adam Shelly wrote:
<snip/>

puts "\nMonkeyPatching!\n\n"
class String
  alias :old_percent :%
  def % arg
    arr = (arg.kind_of? Array) ? arg : [arg]
    target = self.gsub(/%\((.+?)\)/){|m|
      raise ArgumentError, "named parameters need hash" unless
arg.kind_of? Hash
      arr << arg[$1.to_sym]
      "%#{arr.size}$"
    }
    target.old_percent arr
  end
end

<snip/>

Don't forget the escaped %, and also, I propose the format %foo:s
instead of %(foo)s. And heres some more code to chew on, based off of
Adam's:

class String
  alias old_percent %
  def % arg
    names =
    target = gsub /(^|[^%])%(\w+):confused: do
      name, space = $2, $1 # don't forget to replace whatever wasn't %
      names << name unless names.include? name
      "#{space}%#{names.rindex(name) + 1}$"
    end
    args = case arg
      when Hash: names.map {|n| arg[n] or arg[n.to_sym]} #keys are often
symbols
      when Array: arg
      else [arg]
    end
    old_percent args
  end
end

···

--
Posted via http://www.ruby-forum.com/\.

I forgot one important point... Don't make me use a hash if I don't want
to! (hence mapping the hash to array or just using an array if no hash)

···

--
Posted via http://www.ruby-forum.com/.