I’m updating an old Perl script to Ruby and having a problem with finding a replacement for one Perl library.
In the Perl script we use Net::CIDR::Lite, which takes a start and end ip address range and outputs a CIDR string.
This is a Perl example that shows the functionality:
#!/usr/bin/perl
use Net::CIDR::Lite;
$cidrblocks = Net::CIDR::Lite->new;
$cidrblocks->add_range("109.152.0.0-109.152.7.255");
$coveragezone = "";
#cidrlist = $cidrblocks->list();
$cidrcount=0;
while ( defined $cidrlist[$cidrcount] ) {
$coveragezone .= "$cidrlist[$cidrcount]";
}
continue {
$cidrcount++;
}
print "$coveragezone";
This script returns a string:
=> 109.152.0.0/21
Does anyone know of a Ruby lib or gem I could use to duplicate the functionality of the add_range call?
$cidrblocks = Net::CIDR::Lite->new;
$cidrblocks->add_range("109.152.0.0-109.152.7.255");
You can either use the built-in IPAddr class which also handles CIDR networks, or you use the ipaddress gem which provides some additional helpers.
A simple example would be:
cidrblocks = []
cidrblocks << IPAddr.new("109.152.0.0/21")
included = cidrblocks.find{|net| net.include?("109.152.6.123") }
I prefer the NetAddr gem, in particular its NetAddr::CIDR class.
It's a very rich IPv4/IPv6 gem.
Looking through the three modules I know of, Ruby's built-in IPAddr, NetAddr and IPAddress, mentioned by #holgerjust, none of them give us the ability to supply a start IP and end IP and return the resulting network. They all assume a CIDR form of the network, and then work toward testing the individual IPs to see if they fit into the subnet, or using that subnet definition to generate the IPs themselves.
I found a ruby module which does this translation see
http://wejn.org/stuff/cidr.rb.html
have included it in my rails app and it's perfect
Can't find a suitable gem?
Do what comes naturally - reimpliment the Perl module in Ruby.
While this may not be entirely relevant to your question, I ported part of CIDR::Lite to Ruby:
https://github.com/noahhaon/cidr-lite-ruby
I found its performance to be far better than the available ruby alternatives at the time for merging very large sets of overlapping CIDRs.
HTH
Related
So I'm working on a crawler to get a bunch of images on a page that are saved as links. The relevant code, at the moment, is:
def parse_html(html)
html_doc = Nokogiri::HTML(html)
nodes = html_doc.xpath("//a[#href]")
nodes.inject([]) do |uris, node|
uris << node.attr('href').strip
end.uniq
end
I am current getting a bunch of links, most of which are images, but not all. I want to narrow down the links before downloading with a regex. So far, I haven't been able to come up with a Ruby-Friendly regex for the job. The best I have is:
^https?:\/\/(?:[a-z0-9\-]+\.)+[a-z]{2,6}(?:/[^\/?]+)+\.(?:jpg|gif|png)$.match(nodes)
Admittedly, I got that regex from someone else, and tried to edit it to work and I'm failing. One of the big problems I'm having is the original Regex I took had a few "#"'s in it, which I don't know if that is a character I can escape, or if Ruby is just going to stop reading at that point. Help much appreciated.
I would consider modifying your XPath to include your logic. For example, if you only wanted the a elements that contained an img you can use the following:
"//a[img][#href]"
Or even go further and extract just the URIs directly from the href values:
uris = html_doc.xpath("//a[img]/#href").map(&:value)
As some have said, you may not want to use Regex for this, but if you're determined to:
^http(s?):\/\/.*\.(jpeg|jpg|gif|png)
Is a pretty simple one that will grab anything beginning with http or https and ending with one of the file extensions listed. You should be able to figure out how to extend this one, Rubular.com is good for experimenting with these.
Regexp is a very powerful tool but - compared to simple string comparisons - they are pretty slow.
For your simple example, I would suggest using a simple condition like:
IMAGE_EXTS = %w[gif jpg png]
if IMAGE_EXTS.any? { |ext| uri.end_with?(ext) }
# ...
In the context of your question, you might want to change your method to:
IMAGE_EXTS = %w[gif jpg png]
def parse_html(html)
uris = []
Nokogiri::HTML(html).xpath("//a[#href]").each do |node|
uri = node.attr('href').strip
uris << uri if IMAGE_EXTS.any? { |ext| uri.end_with?(ext) }
end
uris.uniq
end
I don't know if it can be called an algorithm but i think its close.
I will be pulling data from an API that will have certain words in the title, eg:
Great Software 2.0 Download Now
Buy Great Software for just $10
Great Software Torrent Download
So, i want to do different things based on the presence of certain words such as Download, Buy etc. For eg, if it has the word 'buy' in it, i would like to extract the word buy and the amount value that is present in the title and show it in another div, so in this case it would be "Buy for $10" or "Buy $10" etc. I can do if/else as well but I don't want to use if else because there could be more such conditions in the future. So what i am thinking about is using the send method. eg:
def buy(string)
'Buy for just' + string.scan(/\$\d+/).first
end
def whichkeyword(title)
send (title.scan(/(download|buy)/i)[0][0]).downcase.to_sym, title
end
whichkeyword('Buy this software for $10 now')
is there a better way to do this? Or is this even a good way to do it? Any help would be appreciated
First of all, use send if and only you are to call private method, use public_send otherwise.
In this particular case metaprogramming is an overkill. It requires too much redundant code, plus it requires the code to be changed for new items. I would go with building a hash like:
#hash = { 'buy' => { text: 'Buy for just %{placeholder}', re: /\$\d+/ } }
This hash might be places somewhere outside of the code, e. g. it might be stored in yml file near the code and loaded in advance. That way you might be able to change a behaviour without modifying the code, that is handy for instance in gem.
As we have a hash defined/loaded, I would call the method:
def format string
key = string[/#{Regexp.union(#hash.keys).source}/i].downcase
puts #hash[key][:text] % { placeholder: string[#hash[key][:re]] }
end
Yielding:
▶ format("Buy this software for $10 now")
#⇒ Buy for just $10
There are many advantages over declaring methods, e. g. now matches might contain spaces, you might easily add/remove matchers etc.
First of all, your algorithm can work, but has some troubles in it, like what if no keyword is applied.
I have two solutions for you:
NLP
If you want to do it much more dynamic, you can use NLP - Natural language Processing. NLP will find main words in you sentence and then you can find the good solution for each.
A good gem for that is Treat that you can use with stanford-core-nlp. After processing the data you can find the verbs and even synonyms in the sentence and figure out what to do.
sentence('Buy this software for $10 now').verbs # ['buy']
Simple Hash
This solution is less dynamic, but much more simple. Like you did with the scan, just use Constant to manage your keywords, and the output from them(I would do it with lambdas). you can also add default to the hash
KEYWORDS = Hash.new('Default Title').merge(
buy: -> { },
download: -> { }
)
KEYWORDS[sentence[/(#{KEYWORDS.keys.join('|')})/i].downcase]
I think this solution is good enough.
The only thing that looks strange is scan(/(download|buy)/i)[0][0].
As for me I don't very much like using [] syntax in Ruby.
I think using scan here is not necessary.
What about
def whichkeyword(title)
title =~ /(download|buy)/i
send $1.downcase.to_sym, title unless $1.nil?
end
UPDATE
def whichkeyword(title)
action = title[/(download|buy)/i]
public_send action.downcase.to_sym, title if action
end
I'd like to get a word list from a text file using Ruby. I found how to use regex to parse only words here, so I made a script like following:
src = File.open("text.txt")
word_list = []
src.each do |line|
word_list << line.downcase.split(/[^[:alpha:]]/).delete_if {|x| x == ""}
end
word_list.flatten!.uniq!.sort!
p word_list
And the following is a sample text file text.txt:
TextMate may be the latest craze for developing Ruby on Rails
applications, but Vim is forever. This plugin offers the following
features for Ruby on Rails application development.
Automatically detects buffers containing files from Rails applications, and applies settings to those buffers (and only those
buffers). You can use an autocommand to apply your own custom
settings as well.
Unintrusive. Only files in a Rails application should be affected; regular Ruby scripts are left untouched. Even when enabled, the
plugin should keep out of your way if you're not using its features.
Easy navigation of the Rails directory structure. gf considers context and knows about partials, fixtures, and much more. There are
two commands, :A (alternate) and :R (related) for easy jumping between
files, including favorites like model to migration, template to
helper, and controller to functional test. For more advanced usage,
:Rmodel, :Rview, :Rcontroller, and several other commands are
provided.
As a Ruby novice, I'd like to learn better (more clear, concise, and following conventions) solutions for this problem.
Thanks for any advices and corrections.
A more idiomatic code would be:
word_list = open("text.txt")
.lines
.flat_map { |line| line.downcase.split(/[^[:alpha:]]/).reject(&:empty?) }
.uniq
.sort
# I suppose you want each line and collect the results
word_list = File.open("text.txt").each_line.collect do |line|
# collecting is done via collect above, no need anymore
# .reject(&:empty?) calls .empty? on each element
line.downcase.split(/[^[:alpha:]]/).reject(&:empty?)
# you can chain on blocks as well
end.flatten!.uniq!.sort!
p word_list
I am trying to use the Yahoo Finance Gem, but am not able to get the information I want. When I try to get a quote, it creates a hash, but instead of the individual information (which I am trying to get), it gives a string will all the information in it. Is there a way to receive a single bit of information (such as % change) as a number? I am very new to ruby, so any help would be awesome.
require 'yahoofinance'
YahooFinance.get_quotes(YahooFinance::StandardQuote, 'yhoo') {|i|
puts i.change
puts i.changePoints
puts i.changePercent
puts i.time
}
Prints for me:
-0.03 - -0.17%
-0.03
-0.17
10:55am
or
r = yahooFinance.get_quotes(YahooFinance::StandardQuote, 'yhoo')
puts r[r.keys[0]].dayHigh
puts r["YHOO"].dayHigh
prints:
17.43
17.43
YahooFinance.get_quotes return a hash in which quote symbols are keys, and all data for each quote is a value. See YahooFinance::BaseQuote class to guess why it is possible to use getters like dayHigh() to auto parse data from the hash value.
I'm running Rails 3.2.8 along with the Ruby 1.9.3 and was having some problems with this gem.
So I just went straight to the source code and took that one file (its just a single file, and short too) and placed it in my /lib folder. In case you haven't been using your lib folder, you must add something like config.autoload_paths += Dir["#{config.root}/lib/**/"] to config/application.rb in order to load up lib folder classes from the rail console or elsewhere in rails.
Besides, its probably the simplest source code you will find and its always good to start reading the actual source that you rely on every day.
What's the best library to use to convert the HTTP POST string received from a browser into a Ruby hash? I don't want to use the large rails-based libraries. I am using eventmachine and evma_httpserver, and want to include the lightest library possible that will decode and convert the params string.
Note: I don't need a webserver. I have the encoded post string in hand, and just need to convert it to a hash.
URI.decode_www_form from the Ruby standard library can do this: http://rubydoc.info/docs/ruby-stdlib/1.9.2/URI#decode_www_form-class_method
You could use the rack gem for its Rack::Utils.parse_query method.
If you want lighter than that, you could just copy the source code to the parse_query and unescape methods from it.
If you want event lighter (but perhaps not as performant or robust) than that, just implement your own split and lean on CGI.unescape.
Try this:
require "uri"
result = URI.decode_www_form("your=post¶ms=values").inject({}) {|r, (key,value)| r[key.to_sym] = value;r}
puts result[:your]
puts result[:params]