A Ruby block question

I am in need of making a method that accepts a block. It basicly needs
to do the following:

* The method it self should output some content first.
* Then after that content it should output the content defined in
&block.
* And at last output some other content defined by the method it self.

In other words I need to put user-defined input in between some
pre-defined content. How can I approach that?

···

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

Alle Thursday 16 October 2008, David Trasbo ha scritto:

I am in need of making a method that accepts a block. It basicly needs
to do the following:

* The method it self should output some content first.
* Then after that content it should output the content defined in
&block.
* And at last output some other content defined by the method it self.

In other words I need to put user-defined input in between some
pre-defined content. How can I approach that?

If I understand correctly what you want to do, you don't need to do anything
special:

def my_method
  puts "predefined text 1"
  puts yield
  puts "predefined text 2"
end

Stefano

Look at yield?

def foo(&block)
  puts "Oh Hai"
  yield
  puts "kthxbai"
end

fooi {puts "No Wai!"}

···

On Thu, Oct 16, 2008 at 08:20:34PM +0900, David Trasbo wrote:

I am in need of making a method that accepts a block. It basicly needs
to do the following:

* The method it self should output some content first.
* Then after that content it should output the content defined in
&block.
* And at last output some other content defined by the method it self.

In other words I need to put user-defined input in between some
pre-defined content. How can I approach that?

--
nathan
nathan_at_nathanpowell_dot_org

Another flaw in the human character is that everybody wants to build
and nobody wants to do maintenance.
     ~ Kurt Vonnegut
------------------------------------

Stefano Crocco wrote:

In other words I need to put user-defined input in between some
pre-defined content. How can I approach that?

If I understand correctly what you want to do, you don't need to do
anything
special:

def my_method
  puts "predefined text 1"
  puts yield
  puts "predefined text 2"
end

My method looks like this:

def fields_for_setting(namespace, name, &block)
  namespace = namespace.to_s
  name = name.to_s
  id = "settings_#{namespace}_#{name}_"
  m = Builder::XmlMarkup.new :indent => 2
  fields_for "settings", setting =
Setting.find_or_initialize_by_namespace_and_name(namespace, name) do |f|
    m.p do
      unless setting.new_record?
        m << (f.hidden_field :id, :index => nil, :id => id+"id")
      end
      m << (f.hidden_field :namespace, :index => nil, :id =>
id+"namespace")
      m << (f.hidden_field :name, :index => nil, :id => id+"name")
      #puts yield f???
    end
  end
end

The problem is, I'm using Builder::XmlMarkup and the fields_for from
Rails. I want to put some hidden fields AND the text fields defined by
the block into a <p> tag. That means I need to be able to access the f
object provided by fields_for and use it in this method's block.

But how?

···

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

Alle Thursday 16 October 2008, David Trasbo ha scritto:

Stefano Crocco wrote:
>> In other words I need to put user-defined input in between some
>> pre-defined content. How can I approach that?
>
> If I understand correctly what you want to do, you don't need to do
> anything
> special:
>
> def my_method
> puts "predefined text 1"
> puts yield
> puts "predefined text 2"
> end

My method looks like this:

def fields_for_setting(namespace, name, &block)
  namespace = namespace.to_s
  name = name.to_s
  id = "settings_#{namespace}_#{name}_"
  m = Builder::XmlMarkup.new :indent => 2
  fields_for "settings", setting =
Setting.find_or_initialize_by_namespace_and_name(namespace, name) do |f|
    m.p do
      unless setting.new_record?
        m << (f.hidden_field :id, :index => nil, :id => id+"id")
      end
      m << (f.hidden_field :namespace, :index => nil, :id =>
id+"namespace")
      m << (f.hidden_field :name, :index => nil, :id => id+"name")
      #puts yield f???
    end
  end
end

The problem is, I'm using Builder::XmlMarkup and the fields_for from
Rails. I want to put some hidden fields AND the text fields defined by
the block into a <p> tag. That means I need to be able to access the f
object provided by fields_for and use it in this method's block.

But how?

I don't know Rails and Builder::XmlMarkup, so I can be completely wrong. From
a quick look at Builder::XmlMarkup api, I'd say that, if the block returns the
string you want to insert in the p element, you only have to insert it in m
like you did for the texts:

m << yield f

Stefano

Stefano Crocco wrote:

But how?

I don't know Rails and Builder::XmlMarkup, so I can be completely wrong.
From
a quick look at Builder::XmlMarkup api, I'd say that, if the block
returns the
string you want to insert in the p element, you only have to insert it
in m
like you did for the texts:

m << yield f

Yes, that was also my first idea, but that is giving me this syntax
error:

syntax error, unexpected tIDENTIFIER, expecting kEND

···

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

Hi --

···

On Thu, 16 Oct 2008, David Trasbo wrote:

Stefano Crocco wrote:

But how?

I don't know Rails and Builder::XmlMarkup, so I can be completely wrong.
From
a quick look at Builder::XmlMarkup api, I'd say that, if the block
returns the
string you want to insert in the p element, you only have to insert it
in m
like you did for the texts:

m << yield f

Yes, that was also my first idea, but that is giving me this syntax
error:

syntax error, unexpected tIDENTIFIER, expecting kEND

Put f in parens:

   m << yield(f)

David

--
Rails training from David A. Black and Ruby Power and Light:
   Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
   Advancing with Rails January 19-22 Fort Lauderdale, FL *
   * Co-taught with Patrick Ewing!
See http://www.rubypal.com for details and updates!

David Trasbo wrote:

syntax error, unexpected tIDENTIFIER, expecting kEND

Then I tried this:

m << (yield f)

But that is giving me this error:

can't convert nil into String

That doesn't really make sence, does it?

···

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

Hi --

···

On Thu, 16 Oct 2008, David Trasbo wrote:

David Trasbo wrote:

syntax error, unexpected tIDENTIFIER, expecting kEND

Then I tried this:

m << (yield f)

But that is giving me this error:

can't convert nil into String

That doesn't really make sence, does it?

Actually it does, because your block contains a call to puts, and puts
always returns nil. You probably want your block just to contain a
string.

David

--
Rails training from David A. Black and Ruby Power and Light:
   Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
   Advancing with Rails January 19-22 Fort Lauderdale, FL *
   * Co-taught with Patrick Ewing!
See http://www.rubypal.com for details and updates!

David A. Black wrote:

m << yield f

Yes, that was also my first idea, but that is giving me this syntax
error:

syntax error, unexpected tIDENTIFIER, expecting kEND

Put f in parens:

   m << yield(f)

That's also a possibility. But it seems to be a little more complicated
as mentioned.

can't convert nil into String

···

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

You're one post behind -- read my previous answer :slight_smile:

David

···

On Thu, 16 Oct 2008, David Trasbo wrote:

David A. Black wrote:

m << yield f

Yes, that was also my first idea, but that is giving me this syntax
error:

syntax error, unexpected tIDENTIFIER, expecting kEND

Put f in parens:

   m << yield(f)

That's also a possibility. But it seems to be a little more complicated
as mentioned.

can't convert nil into String

--
Rails training from David A. Black and Ruby Power and Light:
   Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
   Advancing with Rails January 19-22 Fort Lauderdale, FL *
   * Co-taught with Patrick Ewing!
See http://www.rubypal.com for details and updates!

David A. Black wrote:

Hi --

can't convert nil into String

That doesn't really make sence, does it?

Actually it does, because your block contains a call to puts, and puts
always returns nil. You probably want your block just to contain a
string.

Ah, I see. Let me just explain. Since I'm using Rails I am calling this
method with the block in a view or a template. My template language is
Haml, and this is how it's called:

- fields_for_setting("site", "name") do |f|
  = f.label :value, "Site name", :index => nil
  = f.text_field :value, :index => nil

The = is a short hand for puts as you pointed out. But I don't see other
possibilities than using put, otherwise the text field will not show up
in the view.

How can I both use puts and make some content appear around the text
field?

···

On Thu, 16 Oct 2008, David Trasbo wrote:

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

David Trasbo wrote:

How can I both use puts and make some content appear around the text
field?

I was wondering if concat might suit my needs?

···

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

I'm not a HAML expert, but in general, you don't want to use puts for
anything you're outputting in a template. You just want to insert
strings. I'm not sure how HAML handles string concatenation of this
kind.

David

···

On Thu, 16 Oct 2008, David Trasbo wrote:

David A. Black wrote:

Hi --

On Thu, 16 Oct 2008, David Trasbo wrote:

can't convert nil into String

That doesn't really make sence, does it?

Actually it does, because your block contains a call to puts, and puts
always returns nil. You probably want your block just to contain a
string.

Ah, I see. Let me just explain. Since I'm using Rails I am calling this
method with the block in a view or a template. My template language is
Haml, and this is how it's called:

- fields_for_setting("site", "name") do |f|
= f.label :value, "Site name", :index => nil
= f.text_field :value, :index => nil

The = is a short hand for puts as you pointed out. But I don't see other
possibilities than using put, otherwise the text field will not show up
in the view.

How can I both use puts and make some content appear around the text
field?

--
Rails training from David A. Black and Ruby Power and Light:
   Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
   Advancing with Rails January 19-22 Fort Lauderdale, FL *
   * Co-taught with Patrick Ewing!
See http://www.rubypal.com for details and updates!

David A. Black wrote:

How can I both use puts and make some content appear around the text
field?

I'm not a HAML expert, but in general, you don't want to use puts for
anything you're outputting in a template. You just want to insert
strings. I'm not sure how HAML handles string concatenation of this
kind.

Phew, this got a little more complicated than I hoped. To make this
understandable I'll just explain the problem. I want a method that
accepts a block. Inside the method I'm using the method fields_for that
also accepts a block. I want the f object provided by fields_for passed
over to the block that MY method accepts, like this:

fields_for_setting("site", "name") do |f|
#--------------------------------------^
#This should be the f object that fields_for provides.
  = f.text_field :value, :index => nil

I simply want to reuse the f object to make my view shorter and cleaner.
Both in the method where the fields_for is used and in the view, where
it should be reused.

···

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

David Trasbo wrote:

Actually I think I'm close to a solution now. I made these two methods:

def fields_for_setting(namespace, name)
  id = "settings_#{namespace}_#{name}_"
  m = Builder::XmlMarkup.new :indent => 2
  fields_for "settings[]", setting =
Setting.find_or_initialize_by_namespace_and_name(namespace, name) do |f|
    m.p do
      unless setting.new_record?
        m << (f.hidden_field :id, :index => nil, :id => id+"id")
      end
      m << (f.hidden_field :namespace, :index => nil, :id =>
id+"namespace")
      m << (f.hidden_field :name, :index => nil, :id => id+"name")
      m << yield(f)
    end
  end
end

def text_field_for_setting(namespace, name, label=nil)
  namespace = namespace.to_s
  name = name.to_s
  label ||= "#{namespace.capitalize} #{name}"
  id = "settings_#{namespace}_#{name}_"
  fields_for_setting(namespace, name) do |f|
    f.label :value, label, :index => nil, :for => id+"value"
    f.text_field :value, :index => nil, :id => id+"value"
  end
end

In my view I'm calling the second method (text_field_for_setting) like
this:

= text_field_for_setting(:site, :owner)

The method that is called here then calls my first method
(fields_for_setting) with a block. This actually works fine, but for
some reason that I can't figure out (I hope you can help me) It only the
LAST line of the block that is passed to fields_for_setting that shows
up.

In other words, even though I declare both a label and a text field,
only the text field shows up. How comes?

···

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

Hi --

David Trasbo wrote:

Actually I think I'm close to a solution now. I made these two methods:

def fields_for_setting(namespace, name)
id = "settings_#{namespace}_#{name}_"
m = Builder::XmlMarkup.new :indent => 2
fields_for "settings", setting =
Setting.find_or_initialize_by_namespace_and_name(namespace, name) do |f|
   m.p do
     unless setting.new_record?
       m << (f.hidden_field :id, :index => nil, :id => id+"id")
     end
     m << (f.hidden_field :namespace, :index => nil, :id =>
id+"namespace")
     m << (f.hidden_field :name, :index => nil, :id => id+"name")

That syntax will work but it would be much more idiomatic to do:

   m << f.hidden_field(:name, :index ...)

     m << yield(f)
   end
end
end

def text_field_for_setting(namespace, name, label=nil)
namespace = namespace.to_s
name = name.to_s
label ||= "#{namespace.capitalize} #{name}"
id = "settings_#{namespace}_#{name}_"
fields_for_setting(namespace, name) do |f|
   f.label :value, label, :index => nil, :for => id+"value"
   f.text_field :value, :index => nil, :id => id+"value"
end
end

In my view I'm calling the second method (text_field_for_setting) like
this:

= text_field_for_setting(:site, :owner)

The method that is called here then calls my first method
(fields_for_setting) with a block. This actually works fine, but for
some reason that I can't figure out (I hope you can help me) It only the
LAST line of the block that is passed to fields_for_setting that shows
up.

In other words, even though I declare both a label and a text field,
only the text field shows up. How comes?

Because a block, like a method call, evaluates to the last expression
inside it. For example:

   def get_string
     str = yield
     puts str
   end

   get_string do
     "First string"
     "Second string"
   end

This will print "Second string". "First string" is just thrown away.

So maybe inside your block you want to concatenate the two strings.

David

···

On Thu, 16 Oct 2008, David Trasbo wrote:

--
Rails training from David A. Black and Ruby Power and Light:
   Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
   Advancing with Rails January 19-22 Fort Lauderdale, FL *
   * Co-taught with Patrick Ewing!
See http://www.rubypal.com for details and updates!

David A. Black wrote:

In other words, even though I declare both a label and a text field,
only the text field shows up. How comes?

Because a block, like a method call, evaluates to the last expression
inside it. For example:

Okay, I see.

So maybe inside your block you want to concatenate the two strings.

Okay, I'm using #concat instead. It works just fine, thanks!

···

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

David Trasbo wrote:

So maybe inside your block you want to concatenate the two strings.

Okay, I'm using #concat instead. It works just fine, thanks!

I must have overseen something. Today it doesn't work very well. I do
get my label and text field but I don't get the surrounding <p> tag and
the hidden fields. My methods looks like this:

def fields_for_setting(namespace, name)
  id = "settings_#{namespace}_#{name}_"
  m = Builder::XmlMarkup.new :indent => 2
  fields_for "settings", setting =
Setting.find_or_initialize_by_namespace_and_name(namespace, name) do |f|
    m.p do
      unless setting.new_record?
        m << (f.hidden_field :id, :index => nil, :id => id+"id")
      end
      m << (f.hidden_field :namespace, :index => nil, :id =>
id+"namespace")
      m << (f.hidden_field :name, :index => nil, :id => id+"name")
      m << yield(f)
    end
  end
end

def text_field_for_setting(namespace, name, label=nil)
  namespace = namespace.to_s
  name = name.to_s
  label ||= "#{namespace.capitalize} #{name}"
  id = "settings_#{namespace}_#{name}_"
  fields_for_setting(namespace, name) do |f|
    concat f.label :value, label, :index => nil, :for => id+"value"
    concat f.text_field :value, :index => nil, :id => id+"value"
  end
end

So, I'm concatenating the label and text field inside the
fields_for_setting block. But the fields_for_setting block doesn't have
any effect at all! I think it's because fields_for_setting is only
_returning_ the <p> and hidden fields, but how is this solved?

···

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

Hi --

David Trasbo wrote:

So maybe inside your block you want to concatenate the two strings.

Okay, I'm using #concat instead. It works just fine, thanks!

I must have overseen something. Today it doesn't work very well. I do
get my label and text field but I don't get the surrounding <p> tag and
the hidden fields. My methods looks like this:

def fields_for_setting(namespace, name)
id = "settings_#{namespace}_#{name}_"
m = Builder::XmlMarkup.new :indent => 2
fields_for "settings", setting =
Setting.find_or_initialize_by_namespace_and_name(namespace, name) do |f|
   m.p do
     unless setting.new_record?
       m << (f.hidden_field :id, :index => nil, :id => id+"id")

     end
     m << (f.hidden_field :namespace, :index => nil, :id =>
id+"namespace")
     m << (f.hidden_field :name, :index => nil, :id => id+"name")
     m << yield(f)
   end
end
end

def text_field_for_setting(namespace, name, label=nil)
namespace = namespace.to_s
name = name.to_s
label ||= "#{namespace.capitalize} #{name}"
id = "settings_#{namespace}_#{name}_"
fields_for_setting(namespace, name) do |f|
   concat f.label :value, label, :index => nil, :for => id+"value"
   concat f.text_field :value, :index => nil, :id => id+"value"
end
end

So, I'm concatenating the label and text field inside the
fields_for_setting block. But the fields_for_setting block doesn't have
any effect at all! I think it's because fields_for_setting is only
_returning_ the <p> and hidden fields, but how is this solved?

Try this:

   fields_for_setting(namespace, name) do |f|
     f.label :value, label, :index => nil, :for => id+"value" +
     f.text_field :value, :index => nil, :id => id+"value"
   end

i.e., returning a string from the block.

David

···

On Fri, 17 Oct 2008, David Trasbo wrote:

--
Rails training from David A. Black and Ruby Power and Light:
   Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
   Advancing with Rails January 19-22 Fort Lauderdale, FL *
   * Co-taught with Patrick Ewing!
See http://www.rubypal.com for details and updates!