Could someone please explain the magic in this code

Discussion in 'Ruby' started by John Small, Feb 1, 2009.

  1. John Small

    John Small Guest

    I need to use erb templates so I went to look at the documentation. It
    all seems straight forwards until I get to the example where a lot of
    new coding concepts are pulled in and I'm left totally mystified about
    what's going on.

    The code, from the api documentation is
    <pre>
    require "erb"

    # build data class
    class Listings
    PRODUCT = { :name => "Chicken Fried Steak",
    :desc => "A well messages pattie, breaded and fried.",
    :cost => 9.95 }

    attr_reader :product, :price

    def initialize( product = "", price = "" )
    @product = product
    @price = price
    end

    def build
    b = binding
    # create and run templates, filling member data variebles
    ERB.new(<<-'END_PRODUCT'.gsub(/^\s+/, ""), 0, "",
    "@product").result b
    <%= PRODUCT[:name] %>
    <%= PRODUCT[:desc] %>
    END_PRODUCT
    ERB.new(<<-'END_PRICE'.gsub(/^\s+/, ""), 0, "", "@price").result b
    <%= PRODUCT[:name] %> -- <%= PRODUCT[:cost] %>
    <%= PRODUCT[:desc] %>
    END_PRICE
    end
    end

    # setup template data
    listings = Listings.new
    listings.build

    puts listings.product + "\n" + listings.price

    </pre>
    My questions are;-

    (1) what does "<<-" do just before 'END_PRODUCT'
    (2) how do the lines after .result b get substituted into END_PRODUCT
    and why is there a gsub there. My understanding of the order things are
    happening is clearly deficient because the gsub to remove white space
    will have no effect on 'END_PRODUCT' since there's no white space in it.
    So somehow 'END_PRODUCT' is getting replaced by the lines below, which
    aren't in quotes, before the gsub happens. wtf is going on here?

    (3) How does binding work? The documentation is pretty opaque. What is
    it doing in this context and why is it required?

    All explanations welcome
    --
    Posted via http://www.ruby-forum.com/.
    John Small, Feb 1, 2009
    #1
    1. Advertising

  2. On Feb 1, 2009, at 3:15 PM, John Small wrote:

    > I need to use erb templates so I went to look at the documentation. It
    > all seems straight forwards until I get to the example where a lot of
    > new coding concepts are pulled in and I'm left totally mystified about
    > what's going on.
    >
    > The code, from the api documentation is
    > <pre>
    > require "erb"
    >
    > # build data class
    > class Listings
    > PRODUCT = { :name => "Chicken Fried Steak",
    > :desc => "A well messages pattie, breaded and fried.",
    > :cost => 9.95 }
    >
    > attr_reader :product, :price
    >
    > def initialize( product = "", price = "" )
    > @product = product
    > @price = price
    > end
    >
    > def build
    > b = binding
    > # create and run templates, filling member data variebles
    > ERB.new(<<-'END_PRODUCT'.gsub(/^\s+/, ""), 0, "",
    > "@product").result b
    > <%= PRODUCT[:name] %>
    > <%= PRODUCT[:desc] %>
    > END_PRODUCT
    > ERB.new(<<-'END_PRICE'.gsub(/^\s+/, ""), 0, "", "@price").result b
    > <%= PRODUCT[:name] %> -- <%= PRODUCT[:cost] %>
    > <%= PRODUCT[:desc] %>
    > END_PRICE
    > end
    > end
    >
    > # setup template data
    > listings = Listings.new
    > listings.build
    >
    > puts listings.product + "\n" + listings.price
    >
    > </pre>
    > My questions are;-
    >
    > (1) what does "<<-" do just before 'END_PRODUCT'


    That's a "here document". <<-'END_PRODUCT' means to take the
    following lines up until a line begins with END_PRODUCT as if they
    were a string. The - let's END_PRODUCT be indented from the beginning
    of the line. The single quotes say that no interpolation is to be
    done on the string (that is, it should act like a string literal with
    single quotes).

    >
    > (2) how do the lines after .result b get substituted into END_PRODUCT


    I hope (1) answers that ...

    >
    > and why is there a gsub there.


    The gsub is removing the leading whitespace /^\s+/ from each line.
    This lets the here-document contents be intended to where code would
    normally be.

    > My understanding of the order things are
    > happening is clearly deficient because the gsub to remove white space
    > will have no effect on 'END_PRODUCT' since there's no white space in
    > it.
    > So somehow 'END_PRODUCT' is getting replaced by the lines below, which
    > aren't in quotes, before the gsub happens. wtf is going on here?


    Do you get it now?

    The equivalent would look like:
    ERB.new("<%= PRODUCT[:name] %>\n<%= PRODUCT[:desc] %>\n",
    0, "", "@product").result b
    If the here-document were not used.

    >
    > (3) How does binding work? The documentation is pretty opaque. What is
    > it doing in this context and why is it required?


    You want the PRODUCT, @product, and @price to be evaluated in the
    context of the Listings object rather than the new ERB object just
    created.

    >
    >
    > All explanations welcome
    > --
    > Posted via http://www.ruby-forum.com/.
    >



    I hope this helps.

    -Rob

    Rob Biedenharn http://agileconsultingllc.com
    Rob Biedenharn, Feb 1, 2009
    #2
    1. Advertising

  3. John Small wrote:
    > (1) what does "<<-" do just before 'END_PRODUCT'
    > (2) how do the lines after .result b get substituted into END_PRODUCT


    Search for "here document" in
    http://www.ruby-doc.org/docs/ProgrammingRuby/html/language.html

    It's essentially an inline string literal, from the line following
    <<-FOO until the line which has FOO.

    > (3) How does binding work? The documentation is pretty opaque. What is
    > it doing in this context and why is it required?


    A binding is the environment in which variables are evaluated. If you
    pass a binding, it allows the local variables in your environment to be
    accessed from elsewhere.

    irb(main):001:0> def foo(b)
    irb(main):002:1> puts "x is #{eval("x",b)}"
    irb(main):003:1> end
    => nil
    irb(main):004:0> x = 4
    => 4
    irb(main):005:0> foo(binding)
    x is 4
    => nil

    Note that normally the local variable x would not be accessible inside
    foo, which has its own scope.
    --
    Posted via http://www.ruby-forum.com/.
    Brian Candler, Feb 1, 2009
    #3
  4. John Small

    John Small Guest

    John Small, Feb 2, 2009
    #4
  5. John Small

    John Small Guest

    John Small, Feb 2, 2009
    #5
    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. Gactimus
    Replies:
    22
    Views:
    707
    jeffc
    Dec 15, 2004
  2. Replies:
    5
    Views:
    276
    Bo Persson
    Jan 10, 2006
  3. Alexander Mahone

    Could someone please explain me this error?

    Alexander Mahone, May 2, 2008, in forum: C Programming
    Replies:
    5
    Views:
    264
    Igmar Palsenberg
    May 5, 2008
  4. wkevin
    Replies:
    4
    Views:
    567
    Edward A. Falk
    Nov 7, 2011
  5. Rubyist
    Replies:
    1
    Views:
    92
    Ross Bamford
    Feb 2, 2006
Loading...

Share This Page