string.match doesn't work in Lua for loop - for-loop

I am trying to use a for loop to get author name, id, content of each post in a web forum.
I tried this but it doesn't work. Without the for loop, I will get the first result of the regex in the document.
Any suggestions?
for author_id, author_name, author_joined, author_posts, post_date, post_content in
string.match(content,
"<span class=\"name\"><a name=\"(%d-)\"></a><b>([^<]+).-Joined: ([^<]+).-Posts: ([^<]+).-<span class=\"postdetails\">Posted: ([^<]+).-<span class=\"postbody\">(.-)</span><span class=\"postbody\"></span>")
do
document:appendContent("Name: " .. author_name)
end
end

To be used in a generic for loop, you need an iterator, use string.gmatch (global match) instead of string.match.

Related

IMPORTXML To Return a Value from dividendhistory.org

I want to return the value of "Yield:" from a series of stocks from a URL dividenhistory.org, for example HDIF into a Google sheet using IMPORTXML, where F7 represents the user supplied ticker.
=IMPORTXML("https://dividendhistory.org/payout/TSX/"&F7, "/html/body/div/div[2]/div[2]/p[4]")
The problem with the above is the yield value is not always located in the same paragraph, depending on the ticker. It also returns with the word "Yield:" as part of the value.
I believe I should be using the XPATH parameter which should find and return the yield value only, but I am lost. I am open to all suggestions!
I tried with a few of the tickers there, and this should work. For example:
=IMPORTXML("https://dividendhistory.org/payout/ctas/", "//p[contains(.,'Yield')]/text()")
Output:
Yield: 1.05%
Obviously, you can change 'ctas' for any user input.
Try this and see if it works on all tickers.
EDIT:
To get only the number 1.05, you need to split the result and output the 2nd part:
=index(split(IMPORTXML("https://dividendhistory.org/payout/ctas/", "//p[contains(.,'Yield')]/text()"), ": "),2)
Output:
0.0105

How to use polymorphism to remove a switch statement which compares strings?

I am new to Ruby, so let me describe the context of my problem first:
I have a json as input which has the following key / value pair:
{
"service": "update"
}
The value has many different values for example: insert,delete etc.
Next there is a method x which handles the different requests:
def x(input)
case input[:service]
services = GenericService.new
when "update"
result = services.service(UpdateService.new,input)
when "insert"
result = services.service(InsertService.new,input)
when "delete"
result = services.service(DeleteService.new,input)
....
....
else
raise "Unknown service"
end
puts JSON.pretty_generate(result)
end
What is bothering me is that I still need to use a switch statement to check the String values (reminds me of 'instance of' ugh..). Is there a cleaner way (not need to use a switch)?
Finally I tried to search for an answer to my question and did not succeed, if however I missed it feel free to comment the related question.
Update: I was thinking to maybe cast the string to the related class name as follows: How do I create a class instance from a string name in ruby? and then call result = services.services(x.constantize.new,input) , then the class names ofcourse needs to match the input of the json.
You can try something like:
def x(input)
service_class_name = "#{input[:service].capitalize}Service"
service_class = Kernel.const_get(service_class_name)
service_class.new(input).process
end
In addition you might want to check if this is a valid Service class name at all.
I don't understand why you want to pass the service to GenericService this seems strange. let the service do it's job.
If you're trying to instatiate a class by it's name you're actually speaking about Reflection rather than Polymorphism.
In Ruby you can achieve this in this way:
byName = Object.const_get('YourClassName')
or if you are in a Rails app
byName= 'YourClassName'.constantize
Hope this helps
Just first thoughts, but you can do:
eval(services.service("#{input[:service].capitalize}Service.new, #{input})") if valid_service? input[:service]
def valid_service?
w%(delete update insert).include? input[:service]
end
As folks will no doubt shout, eval needs to be used with alot of care

Sinatra can't convert Symbol into Integer when making MongoDB query

This is a sort of followup to my other MongoDB question about the torrent indexer.
I'm making an open source torrent indexer (like a mini TPB, in essence), and offer both SQLite and MongoDB for backend, currently.
However, I'm having trouble with the MongoDB part of it. In Sinatra, I get when trying to upload a torrent, or search for one.
In uploading, one needs to tag the torrent — and it fails here. The code for adding tags is as follows:
def add_tag(tag)
if $sqlite
unless tag_exists? tag
$db.execute("insert into #{$tag_table} values ( ? )", tag)
end
id = $db.execute("select oid from #{$tag_table} where tag = ?", tag)
return id[0]
elsif $mongo
unless tag_exists? tag
$tag.insert({:tag => tag})
end
return $tag.find({:tag => tag})[:_id] #this is the line it presumably crashes on
end
end
It reaches line 105 (noted above), and then fails. What's going on? Also, as an FYI this might turn into a few other questions as solutions come in.
Thanks!
EDIT
So instead of returning the tag result with [:_id], I changed the block inside the elsif to:
id = $tag.find({:tag => tag})
puts id.inspect
return id
and still get an error. You can see a demo at http://torrent.hypeno.de and the source at http://github.com/tekknolagi/indexer/
Given that you are doing an insert(), the easiest way to get the id is:
id = $tag.insert({:tag => tag})
id will be a BSON::ObjectId, so you can use appropriate methods depending on the return value you want:
return id # BSON::ObjectId('5017cace1d5710170b000001')
return id.to_s # "5017cace1d5710170b000001"
In your original question you are trying to use the Collection.find() method. This returns a Mongo::Cursor, but you are trying to reference the cursor as a document. You need to iterate over the cursor using each or next, eg:
cursor = $tag.find_one({:tag => tag})
return cursor.next['_id'];
If you want a single document, you should be using Collection.find_one().
For example, you can find and return the _id using:
return $tag.find_one({:tag => tag})['_id']
I think the problem here is [:_id]. I dont know much about Mongo but `$tag.find({:tag => tag}) is probably retutning an array and passing a symbol to the [] array operator is not defined.

ruby block questions loop variables

comics = load_comics( '/comics.txt' )
Popup.make do
h1 "Comics on the Web"
list do
comics.each do |name, url|
link name, url
end
end
end
I am new to ruby. This is a piece of code from a ruby website.
I cant find what 'link' and 'list' keyword in the menu.
can someone explain it a little bit those two keywords, and where is the definition of those two keyword .
I am also confused on how they read the variables name and url, they are reading it by the space at the same line or what?
so if I have
Comics1 link_of_comics_site_1
Comics2 link_of_comics_site_2
Comics3 link_of_comics_site_3
so for the first iteration, name=Comics1, and url =link_of_comics_site_1
Thanks.
That's not just Ruby. That's a template for a webpage using ruby add-on methods for HTML generation.
But presumably, the result of the call to load_comics is a Hash, where the keys are names and the values are URLs. You could make one of those yourself:
my_comics_hash = { "name1" => "url1", "name2" => "url2" }
which you can then iterate over the same way:
my_comics_hash.each do |name, url|
puts "Name #{name} goes with URL #{url}"
end
In your code, it's building up an HTML list inside a popup window, but it's the same idea. The each method iterates over a collection - in this case a Hash - and runs some code on every item in that collection - in this case, each key/value pair. When you call each, you pass it a block of code inside do ... end; that's the code that gets run on each item. The current item is passed to the code block, which declares a variable to hold it inside the pipes right after the word do. Since we're iterating over key/value pairs, we can declare two variables, and the key goes in the first and the value in the second.
In ruby function, parenthesis is optional and the ";" end of statement is also optional. ej
link "click here" , "http://myweb.com"
is equivalent to :
link("click here", "http://myweb.com");
But If you have more than one statement in a line the ";" is a must, ej
link("click here1", "http://myweb.com"); link("click here2", "http://myweb.com");
In your code it could be written in
link(name, url)
or just
link(name, url);
or
link name, url
But it is highly recommended to put parenthesis around function parameters for readability unless you have other reason . The ";" is not common in ruby world .

Best way to find nested opening and closing tags

I am making a basic discussion board using ROR. When a user posts a response to a message, the input textarea is prepopulated with the message in quotes using a tag: [QUOTE]. As such the format is:
[QUOTE]quoted message goes here[/QUOTE]
Currently, I have a simple solution that replaces [QUOTE] and [/QUOTE] with HTML using message.sub('[QUOTE]', 'html goes here') as long as [QUOTE] or [/QUOTE] still exist. When I go to respond to a quoted message, I convert the HTML back into the [QUOTE] tag to ensure that the prepopulated input textarea doesn't have HTML in it. As such, a quote of a quote, will look like:
[QUOTE][QUOTE]quoted message here[/QUOTE][/QUOTE]
Here is the problem. If I run my current method again, I will get duplicated HTML fields like:
<div class='test'><div class='test'>quoted message goes here</div></div>
Instead, I want to be able to have a solution that looks like:
<div class='test1'><div class='test2'>quoted message goes here</div></div>
And so on...
Any suggestions on the best way to loop this?
If you want to do depth tracking you'll have to use the block method for gsub:
text = "[QUOTE][QUOTE]quoted message here[/QUOTE][/QUOTE]"
quote_level = 0
new_text = text.gsub(/\[\/?QUOTE\]/) do |m|
case (m)
when '[QUOTE]'
quote_level += 1
"<div class='test#{quote_level}'>"
when '[/QUOTE]'
quote_level -= 1
"</div>"
end
end
puts new_text.inspect
# => "<div class='test1'><div class='test2'>quoted message here</div></div>"
You could make this more robust when handling invalid nesting pairs, but for well-formatted tags this should work.
Here's an idea:
Take this regex
(\[QUOTE\])(.*?)(\[\/QUOTE\])
And apply it to your string. It'll match opening tag, closing tag and content. Then take the content and apply regex again. If there are any matches, that'll be your second level of nesting. Repeat while have matches.
Demo here: http://rubular.com/r/MkGsnUj3vL

Resources