Alter String base class to perform new (private methods) beforereturning itself when called by print

S

Steven G. Harms

class String
def summon
puts "You summon: " + self
end
def summon2
puts "You summon: " + self.gsub('x','y')
end
def to_s
return "Atreyu"
end
end

"Artax".summon
"Artax".summon2

puts "Artax".to_s # reference 1
puts "Artax" # reference 2

This produces...

You summon: Artax
You summon: Artay
Atreyu
Artax

Now the "summon" methods indicate to me that by referencing self in
methods added to the String class, you can alter the string from
within itself. The question is, how can I get `puts "Artax" ` to
produce the same results as `puts "Artax"`. They are both Strings
and, as far as I understand it, to_s is what is used to get a String's
display value.

Question #1: Is there a way to references 1 and 2, supra, to do the
same thing?

Question #2: I thought perhaps I could do the magic on the assignment
operation. I'm not having too much luck with that, as yet.

If this is the best solution, could the responding individual please
be so gracious as to provide example code of how to override
assignment? I'm muddling about with my re-defined initialize
expecting an optional argument, but I've not figured out how to access
it properly.

Thanks,

Steven
 
R

Robert Klemme

class String
def summon
puts "You summon: " + self
end
def summon2
puts "You summon: " + self.gsub('x','y')
end
def to_s
return "Atreyu"
end
end

"Artax".summon
"Artax".summon2

puts "Artax".to_s # reference 1
puts "Artax" # reference 2

This produces...

You summon: Artax
You summon: Artay
Atreyu
Artax

Now the "summon" methods indicate to me that by referencing self in
methods added to the String class, you can alter the string from
within itself.

Actually your summon methods do not alter self.
The question is, how can I get `puts "Artax" ` to
produce the same results as `puts "Artax"`.

??? It's the same statement so I would expect the same output (module
changes of the instance in between invocations).
They are both Strings
and, as far as I understand it, to_s is what is used to get a String's
display value.

It's generally not a good idea to mess with such basic classes like
String in this way (i.e. make to_s return something else than self).
Since your example does not contain context it's difficult to come up
with appropriate suggestions. What are you *really* trying to achieve?
Question #1: Is there a way to references 1 and 2, supra, to do the
same thing?

Not sure what you mean here. Can you explain?
Question #2: I thought perhaps I could do the magic on the assignment
operation. I'm not having too much luck with that, as yet.

You cannot change the semantics of assignment. You can however define
methods that look like assignment, i.e. def foo=(x)...end. I doubt
though that this is helpful for you in this context.
If this is the best solution, could the responding individual please
be so gracious as to provide example code of how to override
assignment? I'm muddling about with my re-defined initialize
expecting an optional argument, but I've not figured out how to access
it properly.

Again, what problem are you trying to solve?

Kind regards

robert
 
T

Todd Benson

class String
def summon
puts "You summon: " + self
end
def summon2
puts "You summon: " + self.gsub('x','y')
end

Keep in mind that these methods output what you want in irb, but
return nil. In my irb session it looks something like this...
irb(main):001:0> class String
irb(main):002:1> def summon
irb(main):003:2> puts "You summon: " + self
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> s = "hello".summon
You summon: hello
=> nil
irb(main):007:0> s
=> nil
def to_s
return "Atreyu"
end
end

"Artax".summon
"Artax".summon2

puts "Artax".to_s # reference 1
puts "Artax" # reference 2

This produces...

You summon: Artax
You summon: Artay
Atreyu
Artax

Now the "summon" methods indicate to me that by referencing self in
methods added to the String class, you can alter the string from
within itself.

Yes, this is possible.
The question is, how can I get `puts "Artax" ` to
produce the same results as `puts "Artax"`. They are both Strings
and, as far as I understand it, to_s is what is used to get a String's
display value.

Question #1: Is there a way to references 1 and 2, supra, to do the
same thing?

Look at these two (using the previous code)...

puts s.to_s
puts(s).to_s
Thanks,

Steven

Todd
 
T

Todd Benson

Look at these two (using the previous code)...

puts s.to_s
puts(s).to_s

No, no , no!! Not using all the previous code! My bad. More "after"
code for you (BTW, Robert's right, you shouldn't really mess with
#to_s)...

class String
def to_s
"hi"
end
end

puts "hello".to_s
puts("hello").to_s

Todd
 
S

Steven G. Harms

Robert, Todd, et. al.,

I apologize if my first post was missing some of the larger context, I
didn't want to have to give too much information about the problem
domain lest that deter replies ;).

The code I'm working on is used for storing Strings which contain
conjugations of Latin verbs ( I mean, people who lived near the
Mediterranean, not people south of the Rio Grande ).

Classical Latin words contain macrons on certain vowels ( the long bar
that signifies a "long" sound ). When following conjugation rules
occasionally the "macron-ized" character needs to be "shortened" and
the macron thus removed. Thus, for example "they love" is the stem of
the infinitive remove_ending("am\={a}re") + "nt" (I'm using LaTeX
style macron representation).

The rules dictate that you should chop off the "re" and add the "nt",
thus giving you "am\={a}nt".

BUT here's the rule, a macronized vowel before 'nt' or 'nd' anywhere
in the string must be shortened, thus the word is *actually* "amant".
There are other conditions that shorten a macron (comes before another
vowel, etc.).

Here's the output (at top) and code (at bottom): http://stevengharms.com/?page_id=1159

While I have the code that produces the "right" output, I'm trying to
refactor the code to be more Ruby-like, cleaner, and more organized.
So the code that I link to here is still functional, but not final
draft!

As it is currently, you enter a verb characterization from the CLI, a
Verb.new object is created with that string as the input. Those
pieces are broken up and you have a Verb object. You can then issue:

demo_verb=Verb.new( verb characterization string )
puts demo_verb.active_present

In 'active_present" 6 strings are created and returned in an array.
Each of these strings are passed to a "check_macron" routine which
removes macrons where needed.

Point #1 ( Original Question, effectively)

My idea was "Well, what if *every* string, in the duration of this
program, knows to check_macron itself at time of assignment OR at time
of being used for output" -- my idea being to "smarten" up String so
that I didn't have to go around invoking check_macron all over the
place. Further, I wouldn't have to change my ( heavy! ) use of the
String assignment idioms ( heavily used ).

Point #2:

I was against the idea of subclassing String because, as i understand
it, my assignments would take the look of:

aLatinString = LatinString.new("something")

instead of the very short and pleasant:

aRegularString = "razzle"

Perhaps I am mistaken in this?

Well, so that's the full story, likely full of a lot of extra details,
but hopefully you will make it through and be able to guide me to more
Ruby like constructions!

Steven
 
R

Robert Klemme

Robert, Todd, et. al.,

I apologize if my first post was missing some of the larger context, I
didn't want to have to give too much information about the problem
domain lest that deter replies ;).

No problem at all. Sometimes it's hard to see where one should draw the
line between too much and too few information to help people understand
what's going on.
The code I'm working on is used for storing Strings which contain
conjugations of Latin verbs ( I mean, people who lived near the
Mediterranean, not people south of the Rio Grande ).

:) My Latin is very rusty nowadays but this brings back memories (our
first sentence in class five was "agricola arat")... :)
Classical Latin words contain macrons on certain vowels ( the long bar
that signifies a "long" sound ). When following conjugation rules
occasionally the "macron-ized" character needs to be "shortened" and
the macron thus removed. Thus, for example "they love" is the stem of
the infinitive remove_ending("am\={a}re") + "nt" (I'm using LaTeX
style macron representation).

The rules dictate that you should chop off the "re" and add the "nt",
thus giving you "am\={a}nt".

BUT here's the rule, a macronized vowel before 'nt' or 'nd' anywhere
in the string must be shortened, thus the word is *actually* "amant".
There are other conditions that shorten a macron (comes before another
vowel, etc.).

Here's the output (at top) and code (at bottom): http://stevengharms.com/?page_id=1159

Where exactly? I could neither see code nor latin words on that page.
While I have the code that produces the "right" output, I'm trying to
refactor the code to be more Ruby-like, cleaner, and more organized.
So the code that I link to here is still functional, but not final
draft!

As it is currently, you enter a verb characterization from the CLI, a
Verb.new object is created with that string as the input. Those
pieces are broken up and you have a Verb object. You can then issue:

demo_verb=Verb.new( verb characterization string )
puts demo_verb.active_present

In 'active_present" 6 strings are created and returned in an array.
Each of these strings are passed to a "check_macron" routine which
removes macrons where needed.

Personally I find this naming a bit unfortunate: checking is usually a
read only operation while you are actually manipulating something.
Point #1 ( Original Question, effectively)

My idea was "Well, what if *every* string, in the duration of this
program, knows to check_macron itself at time of assignment OR at time
of being used for output" -- my idea being to "smarten" up String so
that I didn't have to go around invoking check_macron all over the
place. Further, I wouldn't have to change my ( heavy! ) use of the
String assignment idioms ( heavily used ).

That's double "heavy" - man this must be really heavy. :)

I can see where your motivation comes from. Generally I'd opt for not
putting this into class String because it is too specialized (i.e
functionality that does make sense in context of your application only).

One possible solution that might not too bad from your point of view:
add a class and define a conversion method, e.g.

LatinString = Struct.new :string do
def remove_macron
...
end

def to_s
# for printing...
end
end

class String
def to_latin
LatinString.new self
end
end

now you can do

die = "alea".to_latin

Another alternative (modifying assignment) could be this:

class LatinVocabulary
def initialize
@vars = {}
end

def method_missing(sym, *args, &b)
name = sym.to_s

case
when /^(.*)=$/ =~ name && args.length == 1
# assignment
@vars[$1] = remove_macron(args.first)
when args.empty? && @vars.has_key? name
# getter
@vars[name]
else
super
end
end

def remove_macron(str)
...
end
end

And then

voc = LatinVocabulary.new
voc.die = "alea"
voc.peasant = "agricola"
Point #2:

I was against the idea of subclassing String because, as i understand
it, my assignments would take the look of:

aLatinString = LatinString.new("something")

instead of the very short and pleasant:

aRegularString = "razzle"

Perhaps I am mistaken in this?

No, you are not. How should the interpreter know that "..." suddenly
creates a LatinString and not a String? This is really hard coded into
the language. And it's good that way because otherwise all sorts of
nasty things could happen if anybody could change this.
Well, so that's the full story, likely full of a lot of extra details,
but hopefully you will make it through and be able to guide me to more
Ruby like constructions!

I am still a bit unsure about when those conversions need to be done
because I did not find the code where you indicated. From what you
write method Verb#active_present generates this list you mentioned.
Now, should this list be a list of plain Strings or do you need
LatinStrings to be returned, i.e. does the result of this method call
have to be modified already or do you need to be able to do it later?
If the former, then you can apply the conversion in Verb#active_present,
if the latter you can return a LatinString (as shown above).

But generally, if you need a String with particular properties, create
your own class for this. There are enough options to make handling and
printing of anything as convenient as printing Strings. My 0.03EUR...

Kind regards

robert
 
S

Steven G. Harms

Robert,

Thank you for such a connsidered reply.

:)  My Latin is very rusty nowadays but this brings back memories (our
first sentence in class five was "agricola arat")... :)

It's always about the farmers, isn't it?
Where exactly? I could neither see code nor latin words on that page.

I *believe* you can now see the code at:
http://stevengharms.com/improving-the-latin-command-line-verb-conjugator

I just changed my 'slug' type and failed to understand that the
preview was for admin-eyes-only.
Personally I find this naming a bit unfortunate: checking is usually a
read only operation while you are actually manipulating something.
I see your point on check_macron not being optimally named, let me
keep the old name for purposes of this thread but I'll act on that
recommendation.
I can see where your motivation comes from.  Generally I'd opt for not
putting this into class String because it is too specialized (i.e
functionality that does make sense in context of your application only).
[ code snip ]

That's sensible. I've never seen this Struct.new syntax so I'll be
eager to try it out. I think i ( perhaps cumbersomely? ) wind up
going more-or-less along this design pattern ( now that you can see
the code ).

Another alternative (modifying assignment) could be this:

class LatinVocabulary
   def initialize
     @vars = {}
   end

   def method_missing(sym, *args, &b)
     name = sym.to_s

     case
     when /^(.*)=$/ =~ name && args.length == 1
       # assignment
       @vars[$1] = remove_macron(args.first)
     when args.empty? && @vars.has_key? name
       # getter
       @vars[name]
     else
       super
     end
   end

   def remove_macron(str)
     ...
   end
end

And then

voc = LatinVocabulary.new
voc.die = "alea"
voc.peasant = "agricola"

This feels very "intro to metaprogramming" - the use of the
method_missing method could, in principle, be used to expand the
class. i.e.(pseudocode) "if method_missing :)active_present) do the
active_present build with macron_sanitization routine". This may be
where my code goes to once I get a bit more cleaned up.
No, you are not.  How should the interpreter know that "..." suddenly
creates a LatinString and not a String?  This is really hard coded into
the language.  And it's good that way because otherwise all sorts of
nasty things could happen if anybody could change this.

Heh, well I suppose I was a bit overwhelmed by the idea of open
classes and figured that Ruby would let me saw my hand off on this
point, if I wanted to. That said, I agree it's probably a good thing
that this can't get mucked about with.
LatinStrings to be returned, i.e. does the result of this method call
have to be modified already or do you need to be able to do it later?
If the former, then you can apply the conversion in Verb#active_present,
if the latter you can return a LatinString (as shown above).

I think I wind up doing the former

Thank you for your illuminating replies,

Steven
 
R

Robert Klemme

Thank you for such a connsidered reply.

You're welcome!
It's always about the farmers, isn't it?
:))


I *believe* you can now see the code at:
http://stevengharms.com/improving-the-latin-command-line-verb-conjugator

I can confirm that. :)

Few remarks: your type checking can be simplified:

case input
when Array
....
when String
....
else
raise ArgumentError, "wrong type: #{input.inspect}"
end

Same for the conjugation, for which I would introduce symbols as names
(not "1", "2" etc.).

Same in evaluate_conjugation.

def evaluate_conjugation
case @infinitive
when /\?re$/ # what exactly should your regexp look like?
# question mark without escaping backslash
# is not so good
"1" # or rather a symbol denoting the name
when /...
...
else
end
end

I can see where your motivation comes from. Generally I'd opt for not
putting this into class String because it is too specialized (i.e
functionality that does make sense in context of your application only).
[ code snip ]

That's sensible. I've never seen this Struct.new syntax so I'll be
eager to try it out. I think i ( perhaps cumbersomely? ) wind up
going more-or-less along this design pattern ( now that you can see
the code ).

Struct.new is actually just a convenient way to create a new class with
a set of properties that are honored during #hash, #eql? and #== so
instances can be easily compared and used as Hash keys.

[...]
This feels very "intro to metaprogramming" - the use of the
method_missing method could, in principle, be used to expand the
class. i.e.(pseudocode) "if method_missing :)active_present) do the
active_present build with macron_sanitization routine". This may be
where my code goes to once I get a bit more cleaned up.

If you know the method beforehand it is more efficient to define it
right away.
I think I wind up doing the former

Thank you for your illuminating replies,

You're welcome! I'm glad I could help.

Kind regards

robert
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top