Forum: Ruby Can anyone see the obvious msitake I've made?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Kev J. (Guest)
on 2006-03-29 07:06
(Received via mailing list)
Hi,

I'm hacking up some scripts and I have a lot of repetitive code ie:

#corporate name
@@ie.text_field(:id,
@test_data['pg_401']['corp_nm_fld']).value=@test_data['pg_401']['corp_nm']
@@ie.text_field(:id,
@test_data['pg_401']['corp_nm_kn_fld']).value=@test_data['pg_401']['corp_nm_kn']
#rep name
@@ie.text_field(:id,
@test_data['pg_401']['rep_j_pos_fld']).value=@test_data['pg_401']['rep_j_pos']
@@ie.text_field(:id,
@test_data['pg_401']['rep_nm_fld']).value=@test_data['pg_401']['rep_nm']
@@ie.text_field(:id,
@test_data['pg_401']['rep_nm_kn_fld']).value=@test_data['pg_401']['rep_nm_kn']
#telephone
@@ie.text_field(:id,
@test_data['pg_401']['tel_no_1_fld']).value=@test_data['pg_401']['tel_no_1']
@@ie.text_field(:id,
@test_data['pg_401']['tel_no_2_fld']).value=@test_data['pg_401']['tel_no_2']
...

It looks like a perfect place to use a macro to write out the call to
ie.text_field at run-time (especially because of the duplicated
variables)

I've written a method_missing method in my base class and tried to
implement something like
@@ie.text_field(:id,
@test_data['pg_401']['tel_no_1_fld']).value=@test_data['pg_401']['tel_no_1']
=> text :id, :pg_401, :tel_no_1

This would dramatically reduce the amount of typing (and it'll reduce
the training time for the qc team who ultimately have to write these
scripts), and may allow me to refactor further so that the entire test
script could be constructed from a yml file on the fly - which would
save a *lot* of hassle.

Now I've changed @@ie to $ie as it makes more sense (watir and I only
want to be driving one ie window at once, so make it a global)

in my method_missing definition I have

when /text/=~m.id2name
      #works
      $ie.text_field(:id,
eval("$test_data['#{args[1].to_s}']['#{args[2]}_fld']").to_s).set(eval("$test_data['#{args[1].to_s}']['#{args[2]}']"))
      #doesn't work args[0] = id
      $ie.text_field("#{args[0].to_sym}",
eval("$test_data['#{args[1].to_s}']['#{args[2]}_fld']")).set(eval("$test_data['#{args[1].to_s}']['#{args[2]}']"))

What I want to do is to create a call to $ie.text_field with the correct
params - as you can see if I specify the symbol :id, it works perfectly
(not sure eval is the correct thing to use but it works), however if I
try to get the symbol from the args[] and then to_sym before executing,
I always get

testScenario(NewApplicationTest):
Watir::Exception::UnknownObjectException: Unable to locate object, using
id and
form:address1

However I've just tried (as I was typing this email)
$ie.text_field(eval("args[0].to_sym"),
eval("$test_data['#{args[1].to_s}']['#{args[2]}_fld']")).set(eval("$test_data['#{args[1].to_s}']['#{args[2]}']"))

And it works! :(

Can anyone explain why #{args[0].to_sym} is not actually evaluating to
the :id symbol, but eval(args[0].to_sym) does?  Especially as the error
produced from watir is exactly the same as if I'd have typed:

$ie.text_field(:id, 'a_field_that_doesn't_exist')

Also if there is a better way to do this without using three eval calls
per method that I want to add (seems a little overkill to me)

Thanks
Kev
unknown (Guest)
on 2006-03-29 07:31
(Received via mailing list)
Hi --

On Wed, 29 Mar 2006, Kev J. wrote:

> Can anyone explain why #{args[0].to_sym} is not actually evaluating to the
> :id symbol, but eval(args[0].to_sym) does?  Especially as the error produced
> from watir is exactly the same as if I'd have typed:
>
> $ie.text_field(:id, 'a_field_that_doesn't_exist')

I'm not sure about that part, but I think the problem probably stems
from the fact that "#{'sym'.to_sym}" is a string.  Anything inside the
#{...} construct gets to_s called on it, which trumps the internal
to_sym call.


David

--
David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

"Ruby for Rails" chapters now available
from Manning Early Access Program! http://www.manning.com/books/black
Dave B. (Guest)
on 2006-03-29 09:49
(Received via mailing list)
Kev J. wrote:
> Can anyone see the obvious msitake I've made?

It's "msitake" - should be "mistake" ;)

No, really...

> @@ie.text_field(:id,
> @test_data['pg_401']['tel_no_1_fld']).value=@test_data['pg_401']['tel_no_1']
> => text :id, :pg_401, :tel_no_1
> ...
> Now I've changed @@ie to $ie as it makes more sense (watir and I only want
> to be driving one ie window at once, so make it a global)

This is the obvious solution to me:

# call: text "pg_401", "tel_no_1"
def text(test_id, field_name)
  $ie.text_field(:id, $test_data[test_id][field_name + '_fld']).value =
    $test_data[test_id][field_name]
end

> in my method_missing definition I have
>
> when /text/=~m.id2name

That will match any method call with "text" in the name. Why do you want
that? Just define the method you want ("text").

>      #works
>      $ie.text_field(:id,
> 
eval("$test_data['#{args[1].to_s}']['#{args[2]}_fld']").to_s).set(eval("$test_data['#{args[1].to_s}']['#{args[2]}']"))

You should not need eval to do this. Not at all. The above line is the
same
as:
     $ie.text_field(:id, ($test_data[args[1].to_s][args[2].to_s +
'_fld']).to_s).set(
       $test_data[args[1].to_s][args[2].to_s])

Which you can simplify to this if you assume args[1] and args[2] are
strings:
     $ie.text_field(:id, $test_data[args[1]][args[2] +
'_fld']).set($test_data[args[1]][args[2]])

Summary: less string interpolation, less to_s, and no eval. Keep it
simple,
just deal with variables. You're just making it needlessly complicated
and
confusing yourself.

>      #doesn't work args[0] = id
>      $ie.text_field("#{args[0].to_sym}",
> 
eval("$test_data['#{args[1].to_s}']['#{args[2]}_fld']")).set(eval("$test_data['#{args[1].to_s}']['#{args[2]}']"))

Let's look at this: "#{args[0].to_sym}"

args[0] is being cast to Symbol, then back to String because it's being
interpolated into one.

Hint: "#{x}" is just a strange way of writing x.to_s. If you expect x to
be
a string, it's the same as just plain old x. "Anything in between
quotes"
will always give you a string.

So, removing the evals and string interpolations, we get:
  $ie.text_field(args[0].to_sym.to_s,
$test_data[args[1].to_s][args[2].to_s
+ '_fld']).set(
    $test_data[args[1].to_s][args[2].to_s])

And then we can remove all the to_s: the first one is an error (the
obvious
mistake you're trying to locate), and the rest are unnecessary if we
assume
we're being passed a string:
  $ie.text_field(args[0].to_sym,  $test_data[args[1]][args[2] +
'_fld']).set(
    $test_data[args[1]][args[2]])

A little easier on the eyes?

> What I want to do is to create a call to $ie.text_field with the correct
> params - as you can see if I specify the symbol :id, it works perfectly
> (not sure eval is the correct thing to use but it works), however if I try
> to get the symbol from the args[] and then to_sym before executing, I
> always get
>
> testScenario(NewApplicationTest):
> Watir::Exception::UnknownObjectException: Unable to locate object, using
> id and
> form:address1

I would expect Watir to be a little more helpful by either accepting the
"id" that you're passing, or telling you that it doesn't know how to
locate
objects using a string rather than the symbol it's expecting. I'll ask
Bret
about improving this.

(As I explained earlier in this message, your "to_sym" was interpolated
into
a string, so you were sending a string to text_field.)

> However I've just tried (as I was typing this email)
> $ie.text_field(eval("args[0].to_sym"),
> 
eval("$test_data['#{args[1].to_s}']['#{args[2]}_fld']")).set(eval("$test_data['#{args[1].to_s}']['#{args[2]}']"))
>
> And it works! :(
>
> Can anyone explain why #{args[0].to_sym} is not actually evaluating to the
> :id symbol, but eval(args[0].to_sym) does?  Especially as the error
> produced from watir is exactly the same as if I'd have typed:

"#{1 + 1}" is the same as (1 + 1).to_s, as I explained earlier.
eval("1 + 1") is the same as (1 + 1).

Cheers,
Dave
This topic is locked and can not be replied to.