Q: How to convert hashed parameters to local variables?

Discussion in 'Ruby' started by Dennis Misener, Dec 15, 2003.

  1. In my efforts to write yet another template language {I know, I
    know ..grin}, I find myself wanting to provide simple named
    parameters ala Mason. I'd like to insulate the user from the
    mechanics of "dereferencing" the hashed parameters.

    What I desire is an implementation for "parameters" such that:

    def announce(args)
    parameters(args,:age=>21)
    "My name is #{first} #{last} and my age is #{age}"
    end

    announce:)first=>'John', :last=>'Smith') generates
    "My name is John Smith and my age is 21"

    I was thinking something like ....

    def parameters(args,defaults)
    args=defaults.update(args)
    args.each_pair do |key,value|
    < generate a object with the name 'key' and value 'value'
    in the scope of the calling routine >
    end
    end

    But the <...> portion has eluded me. Any assistance would be
    appreciated.
     
    Dennis Misener, Dec 15, 2003
    #1
    1. Advertising


  2. > def announce(args)
    > parameters(args,:age=>21)
    > "My name is #{first} #{last} and my age is #{age}"
    > end
    >
    > announce:)first=>'John', :last=>'Smith') generates
    > "My name is John Smith and my age is 21"


    You can use:

    args.each { |key,val| eval "#{key}=#{val}" }

    However, this is extremely insecure if you allow joe user to create their own template. They could execute any code they want via it. (#{IO.readlines('/etc/passwd').join}), for example. My suggestion is to write your own template style:

    def announce(args,template)
    template.gsub(/#\((\w+)\)/) { args[$1] }
    end

    or similar.

    - Greg Millam
     
    Gregory Millam, Dec 15, 2003
    #2
    1. Advertising

  3. Dennis Misener

    Jamis Buck Guest

    Dennis Misener wrote:

    >What I desire is an implementation for "parameters" such that:
    >
    >def announce(args)
    > parameters(args,:age=>21)
    > "My name is #{first} #{last} and my age is #{age}"
    >end
    >
    >announce:)first=>'John', :last=>'Smith') generates
    >"My name is John Smith and my age is 21"
    >
    >


    The ostruct solution presented earlier is a nice one. Another
    (eval-based) solution is to simply put each hash-key value in an
    instance variable:

    hash.each_pair do |key,value|
    eval "@#{key} = value"
    end

    'Course, then you have to say "My name is #{@first} and my age is
    #{@age}"...

    --
    Jamis Buck


    ruby -h | ruby -e 'a=[];readlines.join.scan(/-(.)\[e|Kk(\S*)|le.l(..)e|#!(\S*)/) {|r| a << r.compact.first };puts "\n>#{a.join(%q/ /)}<\n\n"'
     
    Jamis Buck, Dec 15, 2003
    #3
  4. Received: Mon, 15 Dec 2003 11:38:30 +0900
    And lo David wrote:
    >
    > The variables you create that way will only be in scope in the
    > eval block itself:
    >
    > eval "a = 1; puts a" # 1
    > puts a # error: a is undefined
    >
    >
    > David
    >


    Not quite; eval creates variables at current scope, But you are right about one thing - the .each |key,val| scope will keep all the variables.

    str = ""
    args.each do |key,val|
    str << "#{key}='#{val}'\n"
    end
    eval str # Will place all the variables at the current scope.

    I still recommend using the regexp search-and-replace instead of that, though. It also doesn't require the string itself to be in the templating function. Not to mention all the ickiness involved with eval, etc.
     
    Gregory Millam, Dec 15, 2003
    #4
  5. Re: How to convert hashed parameters to local variables?

    "Dennis Misener" <> schrieb im Newsbeitrag
    news:...
    > In my efforts to write yet another template language {I know, I
    > know ..grin}, I find myself wanting to provide simple named
    > parameters ala Mason. I'd like to insulate the user from the
    > mechanics of "dereferencing" the hashed parameters.
    >
    > What I desire is an implementation for "parameters" such that:
    >
    > def announce(args)
    > parameters(args,:age=>21)
    > "My name is #{first} #{last} and my age is #{age}"
    > end
    >
    > announce:)first=>'John', :last=>'Smith') generates
    > "My name is John Smith and my age is 21"
    >
    > I was thinking something like ....
    >
    > def parameters(args,defaults)
    > args=defaults.update(args)
    > args.each_pair do |key,value|
    > < generate a object with the name 'key' and value 'value'
    > in the scope of the calling routine >
    > end
    > end
    >
    > But the <...> portion has eluded me. Any assistance would be
    > appreciated.


    Binding and eval are your friends. But they do not fully solve your
    problem, since local variables are checked syntactically for presence:

    def test(hash)
    value = nil
    env = binding

    hash.each do |key,value|
    eval "#{key}=value", env
    end


    puts "last value was #{value}"

    # error because "name" is syntactically checked for presence:
    # puts "name is #{name}"

    hash.each do |key,value|
    eval "printf \"#{key}=%s\n\", #{key}.to_s", env
    end
    end

    irb(main):219:0* test( { :foo => "bar", :name => "Smith" } )
    last value was Smith
    foo=bar
    name=Smith

    Regards

    robert
     
    Robert Klemme, Dec 15, 2003
    #5
  6. Dennis Misener

    Pit Capitain Guest

    Dennis Misener wrote:
    > In my efforts to write yet another template language {I know, I
    > know ..grin}, I find myself wanting to provide simple named
    > parameters ala Mason. I'd like to insulate the user from the
    > mechanics of "dereferencing" the hashed parameters.
    >
    > What I desire is an implementation for "parameters" such that:
    >
    > def announce(args)
    > parameters(args,:age=>21)
    > "My name is #{first} #{last} and my age is #{age}"
    > end
    >
    > announce:)first=>'John', :last=>'Smith') generates
    > "My name is John Smith and my age is 21"


    If you're willing to add "{" and "}" in your announce method you could do:

    def parameters(args,defaults,&blk)
    args=defaults.update(args)
    o = Object.new
    args.each do |key,value|
    o.class.send :define_method, key, proc { value }
    end
    o.instance_eval &blk
    end

    def announce(args)
    parameters(args,:age=>21) {
    "My name is #{first} #{last} and my age is #{age}"
    }
    end

    announce:)first=>'John', :last=>'Smith')

    Note that in the block passed to the method parameters you can only
    access the args (first, last, ...), but not instance variables or
    methods from the calling object.

    HTH

    Regards,
    Pit
     
    Pit Capitain, Dec 15, 2003
    #6
  7. Dennis Misener

    Pit Capitain Guest

    Pit Capitain wrote:

    > ...
    >
    > Note that in the block passed to the method parameters you can only
    > access the args (first, last, ...), but not instance variables or
    > methods from the calling object.


    This wasn't correct: you can even call methods, just not access instance
    variables.

    Regards,
    Pit
     
    Pit Capitain, Dec 15, 2003
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Daniel
    Replies:
    1
    Views:
    1,067
    Kevin Collins
    Aug 14, 2003
  2. Matt Breedlove
    Replies:
    1
    Views:
    439
    Eduard W. Lohmann
    Nov 24, 2003
  3. Tommy

    Is Cookies hashed by default

    Tommy, Aug 6, 2003, in forum: ASP .Net
    Replies:
    0
    Views:
    375
    Tommy
    Aug 6, 2003
  4. =?Utf-8?B?QmlsbCBCb3Jn?=

    Convert hashed password to 128-bit binary

    =?Utf-8?B?QmlsbCBCb3Jn?=, Apr 22, 2005, in forum: ASP .Net
    Replies:
    2
    Views:
    5,432
    =?Utf-8?B?QmlsbCBCb3Jn?=
    Apr 22, 2005
  5. Ian
    Replies:
    3
    Views:
    187
Loading...

Share This Page