I have an array that stores banned IP addresses in my application:
bannedips = ["10.10.10.10", "20.20.20.20", "30.30.30.30"]
I want to add more information to each banned IP address (IP address, ban timestamp, ban reason).
How can I do this in Ruby?
If there are a reasonable number of IPs to be tracked, I'd probably use a simple Hash:
banned_ips = {
"10.10.10.10" => {:timestamp => Time.now, :reason => 'foo'},
"20.20.20.20" => {:timestamp => Time.now, :reason => 'bar'},
"30.30.30.30" => {:timestamp => Time.now, :reason => nil}
}
A hash is a quick and dirty way to create a list that acts like an indexed database; Lookups are extremely fast. And, since you can only have a single instance of a particular key, it keeps you from dealing with duplicate data:
banned_ips["20.20.20.20"] # => {:timestamp=>2015-01-02 12:33:19 -0700, :reason=>"bar"}
banned_ips.keys # => ["10.10.10.10", "20.20.20.20", "30.30.30.30"]
As a general programming tip for choosing arrays vs hashes. If you:
So, for what you want, retrieving values tied to a specific IP, use a hash. An array, or array-of-arrays would cause the code to waste time looking for the particular value and would slow down as new items were added to the array because of those lookups.
There's a point where it becomes more sensible to store this sort of information into a database, and as a developer it's good to learn about them. They're one of many tools we need to have in our toolbox.
banned_ips.include? ip to check if an IP address is banned but what can/should I use to retrieve the additional details?banned_ips["20.20.20.20"] gives you the details hash. To get a particular value from that hash just retrieve it via its key like so: banned_ips["20.20.20.20"][:reason]Yes, multidimensional arrays are possible in Ruby. Arrays can contain any value, so a multidimensional array is just an array which contains other arrays:
banned_ips = [
["10.10.10.10", Date.new(2015, 1, 2), "reason"],
["20.20.20.20", Date.new(2014, 12, 28), "reason"],
["30.30.30.30", Date.new(2014, 12, 29), "reason"],
]
Personally though I wouldn't recommend using a multidimensional array for this purpose. Instead, create a class which encapsulates information about the banned IP.
Simple example:
class BannedIP
attr_reader :ip, :time, :reason
def initialize(ip, time:, reason: "N/A")
@ip = ip
@time = time
@reason = reason
end
end
banned_ips = [
BannedIP.new("10.10.10.10", time: Date.new(2015, 1, 2)),
BannedIP.new("20.20.20.20", time: Date.new(2014, 12, 28)),
BannedIP.new("30.30.30.30", time: Date.new(2014, 12, 29), reason: "Spam"),
]
bannedips = [{ip: "10.10.10.10", reason: "bla bla bla", etc...}, {...}]Or if you want to be super fancy and object-oriented, make aBannedIPclass and store an array of those.bannedips[0][:ip]. Read the documentation.