How to I temporarily override print and puts from a class? - ruby

I'm working on a shell for my project. I want the Shell class to override all print functions while the shell is running, so I've done this:
# WARNING: Blocks until the user exits.
def start
# Override Kernel print functions.
master_print = Kernel.method :print
master_puts = Kernel.method :puts
Kernel.module_exec {
define_method(:print) { |text = ""|
self.send(:print_override, master_print, text)
}
define_method(:puts) { |text = ""|
self.send(:puts_override, master_puts, text)
}
define_method(:puts_padded) { |text = ""|
self.send(:puts_override, master_puts, "")
self.send(:puts_override, master_puts, text)
self.send(:puts_override, master_puts, "")
}
}
# Readline loop and command parsing here...
end
This works well as long as only the Shell class is outputting any text, but as soon as a command class tries to puts I get this:
NoMethodError: undefined method `puts_override' for #<AddressKit::CLI::Interactiv
e::Commands::LoadTable:0x000000027735b0>
I thought that the print and puts blocks I wrote above would stay in this scope, not execute in which ever scope they happened to be called from. Is it possible to fix that?
Bonus question: How do I put the original print functions back? I had planned this:
Kernel.module_exec {
define_method(:print, &master_print)
define_method(:puts, &master_puts)
undef_method(:puts_padded)
}
But I haven't tried it yet, and I don't know if that will leave Kernel exactly as I found it.

While blocks do preserve scope, self is special and refers to the instance running the method (I assume because this is more useful).
An alternative strategy would be to use the fact that Kernel#puts just does $stdout.puts, so instead of overriding puts, set $stdout to one of your classes, that can massage the values before passing them to puts on the former $stdout.
When you're done, restore $stdout to its original value.

Well, I think I fixed the problem, but I'm not sure why it works. I'm guessing that the blocks are in both the scope where it was called from, as well as in the scope they were defined in, but with higher priority on the former. That would mean that I can't use self and expect it to point to the shell. Instead I did this:
shell = self
Kernel.module_exec {
define_method(:print) { |text = ""|
shell.send(:print_override, master_print, text)
}
define_method(:puts) { |text = ""|
shell.send(:puts_override, master_puts, text)
}
define_method(:puts_padded) { |text = ""|
shell.send(:puts_override, master_puts, "")
shell.send(:puts_override, master_puts, text)
shell.send(:puts_override, master_puts, "")
}
}
Which works perfectly but (if I'm right) would break as soon as the variable shell exists in the same scope as print or puts is called from. But it doesn't break, I tested it! I really have no idea what's going on and I'd be very grateful if somebody could explain it :-)

Related

Change label text from within command proc

I have already created a TkLabel beforehand and now I want to update its text variable inside a command proc. The only problem is that NameError: unknown option 'codelabel' for #<Tk::Button:0x00000000050797e8 #path=".w00003"> (deleted widget?).
It seems it is unable to reference the label variable from within the command proc.
Is this the right way to assign?
TkButton.new(root){
text "GET"
#command proc { p txthost.value; p txtpath.value;exit }
command proc{
rc.get_method(txthost.value,txtpath.value);
txtcode.value=rc.code;
txthtml.value=rc.html;
codelabel.text=txtcode.value #label text updated here
}
pack('side'=>'bottom', 'padx'=>10, 'pady'=>10)
}
Any advice?
Forgive me, for the question, I am new to Ruby.
The problematic context (where it's unable to reference the label variable) isn't the command proc. Instead, unexpectedly, it's the entire code block passed to TkButton.new.
Generally speaking, the best way around this problem is to avoid passing a code block to TkButton.new in the first placeā€”as follows:
b = TkButton.new(root)
b.text "GET"
#command proc { p txthost.value; p txtpath.value;exit }
b.command proc{
rc.get_method(txthost.value, txtpath.value)
txtcode.value = rc.code
txthtml.value = rc.html
codelabel.text = txtcode.value #label text updated here
}
b.pack('side'=>'bottom', 'padx'=>10, 'pady'=>10)

Ruby: How to generate lines of code inside a program?

I am developing a parser in Ruby using the parslet library.
The language I am parsing has a lot of keywords that can be merged into a single parsing rule like this:
rule(:keyword) {
str('keyword1') |
str('keyword2') |
str('keyword2') ...
}
Is there a good way to generate this set of lines of code dynamically, by reading a text file with all the keywords?
This would help me keep my parser clean and small, making it easier to add new keywords without modifying the code.
The pseudo-code of what I want to embed inside the rule(:keyword) would be somethings like this:
File.read("keywords.txt").each { |k| write_line " str(\'#{k}\') "}
So far, the workaround I have found is to have a separate ruby program loading the parser code as:
keywords = ["keyword1", "keyword2","keyword3"]
subs = {:keyword_list => keywords .inject("") { |a,k| a << "str('#{k}') | \n"} }
eval( File.read("parser.rb") % subs)
where the parser code has the following lines:
rule(:keywords){
%{keyword_list}
}
Is there a more elegant way to achieve this?
You can try something like this:
rule(:keyword) {
File.readlines("keywords.txt").map { |k| str(k.chomp) }.inject(&:|)
}
In this case, you don't really need to "generate lines of code". As #Uri tried to explain in his answer, there's nothing special about the contents of that rule method; it's just plain Ruby code. Because of this, anything you can do in Ruby you can do inside that rule method as well, including read files, dynamically call methods, and call methods on objects.
Let me break down your existing code, so I can better explain how a dynamic solution to the same problem would work:
rule(:keyword) {
# Stuff here
}
This code right here calls a rule method and passes it :keyword and a block of code. At some point, parslet will call that block and check its return value. Parslet might choose to call the block using instance_exec, which can change the context the block is being executed in to make methods not available outside the block (like str, perhaps) available inside it.
str('keyword1')
Here, inside the context of the rule block, you are calling a method named str with the string "keyword1", and getting the result. Nothing special here, this is just a normal method call.
str('keyword1') | str('keyword2')
Here, the | operator is actually just a method being called on whatever str('keyword1') is returning. This code is equivalent to str('keyword1').send(:'|', str('keyword2')).
str('keyword1') |
str('keyword2') |
str('keyword2')
Same as before, except this time we're calling | on whatever str('keyword1').send(:'|', str('keyword2')) returned. The result of this method call is returned to the rule method when it calls the block.
So now that you know how all this works, you can perform exactly the same operations (calling str with each keyword, and using the | method to "add up" the results) dynamically, based on the contents of a file perhaps:
rule(:keyword) {
File.readlines("keywords.txt").map(&:chomp).map { |k| str(k) }.inject(:|)
}
Breakdown:
rule(:keyword) { # Call the rule method with the `:keyword` argument, and pass
# it this block of code.
File.readlines("keywords.txt"). # Get an array of strings containing all the
# keywords
map(&:chomp). # Remove surrounding whitespace from each keyword in the array,
# by calling `chomp` on them. (The strings returned by
# `File.readlines` include the newline character at the end of
# each string.)
map { |k| str(k) }. # Convert each keyword in the array into whatever is
# returned by calling `str` with that keyword.
inject(:|) # Reduce the returned objects to a single one using the `|`
# method on each object. (Equivalent to obj1 | obj2 | obj3...)
}
And that's it! See? No need to generate any lines of code, just do what the real code is doing, but do it dynamically!

passing value (through variable) to custom function

I'm trying this thing for a while but can't figure out what am I doing wrong.
Here is sample function (which is similar to the original one, except for the hash, which is generated dynamically in the original one):
module Puppet::Parser::Functions
newfunction(:am_running_oss, :type => :rvalue ) do |args|
oss = {:linux=>["Slackware", "RedHat", "Caldera"],
:mac=>["Jaguar", "Lion", "Tiger", "Kodiak"],
:win=>["Chicago", "Daytona", "Longhorn"]}
cls = args[0]
if oss.key?(cls)
return oss[cls][0]
else
return 'undefined'
end
end
end
and then in my manifest, I have this:
$h= am_running_oss($::am_os_type)
notify { "=*=*= amRunningOS <|:|> ${h} =*=*=*=*=*=*=*=": }
(am_os_type is a fact, that returns win, mac or linux based on the node type)
I was expecting to see Jaguar or Slackware as the return value but I get undefined instead. Does anyone know what am I doing wrong? Is there anything still am I missing in terms of passing the args to the function? Cheers!!
Replying to my own question, in case google lands someone here looking for the same. The thing worked for me is to is define cls like this:
cls = args[0].to_sym if args[0].is_a? String
Cheers!!

how to read parameters from console to method in groovy?

I am new to groovy.I am reading values for 2 variables from console with below lines of code.
System.in.withReader {
println "Version: "
version = it.readLine()
println "Doc Type:"
Doc=it.readLine()
call getBillID(version,Doc)
}
getBillid method is as below,
def getBillID(int version,int doc)
{
allNodes.BillID.each {
theregularExpression=/\d+_\d+_\d+_\d_\d+_\d+_\d_${version}_${Doc}_\d+_\d+/
if(it != "" && it =~ theregularExpression) {
println "******" + it
}
}
}
now i want to use those variable values in my getBILLID method but i am getting error as
No signature of method: ReadXML.getBillID() is applicable for argument types: (java.lang.String, java.lang.String) values: [9, ]
where i went wrong.can any one tell me plz..
In addition to #Kalarani's answer, you could also do this:
System.in.withReader {
print "Version: "
int version = it.readLine() as int
print "Doc Type: "
int doc = it.readLine() as int
getBillID( version, doc )
}
As an aside; I would be careful with your capitalisation and variable names, ie: you have a variable called Doc with a capital letter. This is not the standard naming scheme, and you are best using all lowercase for variable names. You can see where it has got confused in the getBillID method. The parameter is called doc (all lowercase), but in the regular expression you reference ${Doc} (uppercase again).
This sort of thing is going to end up causing you a world of pain and bugs that might take you longer to find
Where is the getBillId() method defined? and what is the signature of the method? It would help understanding your problem if you could post that.

groovy while loop syntax assigning and checking a variable

I was reading a blog post and saw a groovy snippet that looked lik
while ( entry = inputStream.nextEntry ) {
// do something
}
In the while loop, is this groovy syntax that will cause the loop to break when entry is null?
Yes, but it will probably make the compiler complain about
a possible accidental assignment. A better practise is:
while ((entry = inputStream.nextEntry )!=null) {}
First week using Groovy and wanted to test this out. Thought I would share the test & results. Thanks for pointing this out.
def list = ['one', 'two', null, 'four']
def it = list.iterator()
def i
while (i = it.next()) {
println i
}
Result: one
two

Resources