I've never done a thing with ruby script before, and was hoping someone here would have a quick answer. I'm on a time sensitive project, and was hoping SO could provide some insight.
I've googled around here, and sought out some ruby script guides, and think I understand most of the following code, but there are a few things I wasn't able to figure out.
I have the following exceprt from a ruby script, and I just need to know what it's doing:
where docName and document_name are a string of a file path
case docName
when /^QRX/ then document_name = "/TRPRR/#{docName}"
when /^BVN/ then document_name = "/TRPRR/#{docName}"
....
There are a bunch of other cases, and I understand case statements. I don't understand the following:
Is the /^QRX/ some kind of regular expression or something? And what does the #{docName} do?
Yes, the /^QRX/, etc. performs a regular expression match against docName, and if it matches, it performs the code following.
The #{docName} is how ruby handles string interpolation:
docName = "foo"
puts "/TRPRR/#{docName}" # Outputs "/TRPRR/foo"
Related
I have limited knowledge of lua and would like to make an or statement.
However, I don't know the exact syntax.
Would the code below work correctly?
if text == "/teamspeak" or text == "/ts" then
If not please let me know on the correct syntax of the statement.
Yes, the statements are correct. You do not have any syntactical errors there, though you might want to check whether text contains only the command or the whole string (as is the case with ptokax). You might also want to check that the command is uppercase/lowercase or mixed-casing.
local sCmd = text:lower()
if sCmd == "/ts" or sCmd == "/teamspeak" then
...
end
Lua uses the keyword or for or statements.
I recommend reading the Lua language reference.
Your code would work correctly if you terminate the if then statement with end.
Best way is to try it yourself. If you do not have Lua installed you can use http://www.lua.org/demo.html
And please note that nil is not the same as false! Many Lua beginners have problems here.
That statement should work, though I suggest converting the string to lowercase first, as jhpotter92 already suggested.
A typical problem in cases like this is when the order the language deals with operands is not the one you'd expect; if, for example, lua were to evaluate the or before the == operator (which it doesn't, see reference) that code would not work. Therefore it is never a bad idea to write your code like this
if (text == "/teamspeak") or (text = "/ts") then <...> end
just to be sure lua does things in the correct order.
If you ever find yourself in this kind of situation again, and you don't want to wait for someone to respond to your question, you can just start lua in interactive mode (assuming you have lua installed on your system, which is very helpful for everyone who wants to learn/code in lua) and type something like
> text = "/teamspeak"
> if text == "/teamspeak" or text == "/ts" then print "true ♥" end
In this example, the console will output "true ♥". Repeat this with text="/ts" and text="some other string" and see if the line of code behaves as it should.
This shouldn't take you longer than 5 minutes (maybe +5 minutes to install lua first)
I am attempting to modify someone's script.
I have managed to modify everything but there is one problem left I am unable to solve:
disp_status("\tAnswer: #{convert_err(results["status"])}")
This produces various outputs as it is run, however, when the output is "ERROR", I want it to do an action. I am not sure how to limit it to "Error", as it appears to always run the method no matter the output.
What I tried was:
if #{convert_err(results["status"])} =~ /ERROR/
and a lot of similar iterations without any luck. Can anyone help?
In ruby interpolation doesn't work without double-quotes. But using interpolation here is an over kill, so just change the line in question from:
if #{convert_err(results["status"])} =~ /ERROR/
To
if convert_err(results["status"]) =~ /ERROR/
And should it should work! :-)
I think the .include? method might be helpful. You could do:
if "#{convert_err(results["status"])}".include?("ERROR")
Furthermore if convert_err returns a string you could just call:
if convert_err(results["status"]).include?("ERROR")
And another option would be to call .to_s which will convert the result of convert_err to a string. So that would look like:
if convert_err(results["status"]).to_s.include?("ERROR")
For further reference read: http://www.ruby-doc.org/core-2.1.4/String.html#method-i-include-3F
Not sure if this is possible but can I call a method from an irb shell with spaces between parameters rather than commas (don't ask) ? Lets say I have a method
def start_band(member1, member2, member3, member4)
#do something
end
And then I call it like the following:
irb>> start_band "John" "Paul" "George" "Ringo"
EDIT: Would it be possible to detect every keypress instead?
No, you can't do that. Not with strings anyway.
No.
You could use something like treetop to write a really simple DSL, or just play monkey-parsing games, but that won't solve your exact question.
The other obvious answer is this, which also fails:
irb>> start_band %W(John Paul George Ringo)
Creating an irb-like CLI isn't difficult, and may be adequate, depending on what your actual requirements are.
There is actually a very easy way to get rid of the commas. You can even get rid of the quotes, too:
def start_band(members)
#members is an array
end
start_band %w(John Paul George Ringo)
The limitation is that you can't use spaces inside your strings, and you still need start-end terminations (can use other characters instead of parenthesis though).
Durr! I really approached this the wrong way. I simply needed to run
#members = gets
to allow the input as required. Thanks for the responses nonetheless.
A Ruby dev I know asked this; my answer is below... Are there other, better reasons?
Why do so many Ruby programmers do
"#{string}"
rather than
string
since the second form is simpler and more efficient?
Is this a common idiom for Ruby developers? I don't see it that much.
Smaller changes when you later need to do more than simply get the value of the string, but also prepend/append to it at the point of use seems to be the best motivation I can find for that idiom.
There is only one case where this is a recommended idiom :
fname = 'john'
lname = 'doe'
name = "#{fname} #{lname}"
The code above is more efficient than :
name = fname + ' ' + lname
or
name = [fname, lname].join(' ')
What's the broader context of some of the usages? The only thing I can come up with beyond what's already been mentioned is as a loose attempt at type safety; that is, you may receive anything as an argument, and this could ensure that whatever you pass in walks like a duck..or, well, a string (though string.to_s would arguably be clearer).
In general though, this is probably a code smell that someone along the way thought was Best Practices.
I use this kind of code, so that I can pass nil as string and it still will work on a string, rather than seeing some exceptions flying:
def short(string = nil)
"#{string}"[0..7]
end
And it's easier/faster to append some debug code, if it's already in quotes.
So in short: It's more convenient.
Interesting answers, everyone. I'm the developer who asked the original question. To give some more context, I see this occasionally at my current job, and also sometimes in sample code on the Rails list, with variables that are known in advance to contain strings. I could sort of understand it as a substitute for to_s, but I don't think that's what's going on here; I think people just forget that you don't need the interpolation syntax if you're just passing a string variable.
If anyone tried to tell me this was a best practice, I'd run away at top speed.
maybe it is easy way to convert any to string? Because it is the same as call to_s method. But it is quite strange way :).
a = [1,2,3]
"#{a}"
#=> "123"
a.to_s
#=> "123"
I could image this being useful in cases where the object being interpolated is not always a String, as the interpolation implicitly calls #to_s:
"#{'bla'}" => "bla"
"#{%r([a-z])}" => "(?-mix:[a-z])"
"#{{:bla => :blub}}" => "blablub"
May make sense when logging something, where you don't care so much about the output format, but never want an error because of a wrong argument type.
I'm interested in building a DSL in Ruby for use in parsing microblog updates. Specifically, I thought that I could translate text into a Ruby string in the same way as the Rails gem allows "4.days.ago". I already have regex code that will translate the text
#USER_A: give X points to #USER_B for accomplishing some task
#USER_B: take Y points from #USER_A for not giving me enough points
into something like
Scorekeeper.new.give(x).to("USER_B").for("accomplishing some task").giver("USER_A")
Scorekeeper.new.take(x).from("USER_A").for("not giving me enough points").giver("USER_B")
It's acceptable to me to formalize the syntax of the updates so that only standardized text is provided and parsed, allowing me to smartly process updates. Thus, it seems it's more a question of how to implement the DSL class. I have the following stub class (removed all error checking and replaced some with comments to minimize paste):
class Scorekeeper
attr_accessor :score, :user, :reason, :sender
def give(num)
# Can 'give 4' or can 'give a -5'; ensure 'to' called
self.score = num
self
end
def take(num)
# ensure negative and 'from' called
self.score = num < 0 ? num : num * -1
self
end
def plus
self.score > 0
end
def to (str)
self.user = str
self
end
def from(str)
self.user = str
self
end
def for(str)
self.reason = str
self
end
def giver(str)
self.sender = str
self
end
def command
str = plus ? "giving ##{user} #{score} points" : "taking #{score * -1} points from ##{user}"
"##{sender} is #{str} for #{reason}"
end
end
Running the following commands:
t = eval('Scorekeeper.new.take(4).from("USER_A").for("not giving me enough points").giver("USER_B")')
p t.command
p t.inspect
Yields the expected results:
"#USER_B is taking 4 points from #USER_A for not giving me enough points"
"#<Scorekeeper:0x100152010 #reason=\"not giving me enough points\", #user=\"USER_A\", #score=4, #sender=\"USER_B\">"
So my question is mainly, am I doing anything to shoot myself in the foot by building upon this implementation? Does anyone have any examples for improvement in the DSL class itself or any warnings for me?
BTW, to get the eval string, I'm mostly using sub/gsub and regex, I figured that's the easiest way, but I could be wrong.
Am I understanding you correctly: you want to take a string from a user and cause it to trigger some behavior?
Based on the two examples you listed, you probably can get by with using regular expressions.
For example, to parse this example:
#USER_A: give X points to #USER_B for accomplishing some task
With Ruby:
input = "#abe: give 2 points to #bob for writing clean code"
PATTERN = /^#(.+?): give ([0-9]+) points to #(.+?) for (.+?)$/
input =~ PATTERN
user_a = $~[1] # => "abe"
x = $~[2] # => "2"
user_b = $~[3] # => "bob"
why = $~[4] # => "writing clean code"
But if there is more complexity, at some point you might find it easier and more maintainable to use a real parser. If you want a parser that works well with Ruby, I recommend Treetop: http://treetop.rubyforge.org/
The idea of taking a string and converting it to code to be evaled makes me nervous. Using eval is a big risk and should be avoided if possible. There are other ways to accomplish your goal. I'll be happy to give some ideas if you want.
A question about the DSL you suggest: are you going to use it natively in another part of your application? Or do just plan on using it as part of the process to convert the string into the behavior you want? I'm not sure what is best without knowing more, but you may not need the DSL if you are just parsing the strings.
This echoes some of my thoughts on a tangental project (an old-style text MOO).
I'm not convinced that a compiler-style parser is going to be the best way for the program to deal with the vaguaries of english text. My current thoughts have me splitting up the understanding of english into seperate objects -- so a box understands "open box" but not "press button", etc. -- and then having the objects use some sort of DSL to call centralised code that actually makes things happen.
I'm not sure that you've got to the point where you understand how the DSL is actually going to help you. Maybe you need to look at how the english text gets turned into DSL, first. I'm not saying that you don't need a DSL; you might very well be right.
As for hints as to how to do that? Well, I think if I were you I would be looking for specific verbs. Each verb would "know" what sort of thing it should expect from the text around it. So in your example "to" and "from" would expect a user immediately following.
This isn't especially divergent from the code you've posted here, IMO.
You might get some milage out of looking at the answers to my question. One commenter pointed me to the Interpreter Pattern, which I found especially enlightening: there's a nice Ruby example here.
Building on #David_James' answer, I've come up with a regex-only solution to this since I'm not actually using the DSL anywhere else to build scores and am merely parsing out points to users. I've got two patterns that I'll use to search:
SEARCH_STRING = "#Scorekeeper give a healthy 4 to the great #USER_A for doing something
really cool.Then give the friendly #USER_B a healthy five points for working on this.
Then take seven points from the jerk #USER_C."
PATTERN_A = /\b(give|take)[\s\w]*([+-]?[0-9]|one|two|three|four|five|six|seven|eight|nine|ten)[\s\w]*\b(to|from)[\s\w]*#([a-zA-Z0-9_]*)\b/i
PATTERN_B = /\bgive[\s\w]*#([a-zA-Z0-9_]*)\b[\s\w]*([+-]?[0-9]|one|two|three|four|five|six|seven|eight|nine|ten)/i
SEARCH_STRING.scan(PATTERN_A) # => [["give", "4", "to", "USER_A"],
# ["take", "seven", "from", "USER_C"]]
SEARCH_STRING.scan(PATTERN_B) # => [["USER_B", "five"]]
The regex might be cleaned up a bit, but this allows me to have syntax that allows a few fun adjectives while still pulling the core information using both "name->points" and "points->name" syntaxes. It does not allow me to grab the reason, but that's so complex that for now I'm going to just store the entire update, since the whole update will be related to the context of each score anyway in all but outlier cases. Getting the "giver" username can be done elsewhere as well.
I've written up a description of these expressions as well, in hopes that other people might find that useful (and so that I can go back to it and remember what that long string of gobbledygook means :)