A Ruby block question

D

David Trasbo

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?
 
S

Stefano Crocco

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
 
N

Nathan Powell

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?

Look at yield?


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

fooi {puts "No Wai!"}

--
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
------------------------------------
 
D

David Trasbo

Stefano said:
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?
 
S

Stefano Crocco

Alle Thursday 16 October 2008, David Trasbo ha scritto:
Stefano said:
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
 
D

David Trasbo

Stefano said:
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
 
D

David A. Black

Hi --

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!
 
D

David Trasbo

David said:
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?
 
D

David A. Black

Hi --

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!
 
D

David Trasbo

David said:
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
 
D

David A. Black

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

can't convert nil into String

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


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!
 
D

David Trasbo

David said:
Hi --



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?
 
D

David A. Black

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?

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

--
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!
 
D

David Trasbo

David said:
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.
 
D

David Trasbo

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, :eek:wner)

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?
 
D

David A. Black

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, :eek:wner)

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

--
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!
 
D

David Trasbo

David said:
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!
 
D

David Trasbo

David said:
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?
 
D

David A. Black

Hi --

David said:
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

--
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!
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,197
Messages
2,571,041
Members
47,643
Latest member
ashutoshjha_1101

Latest Threads

Top