array.sort_by { |s| [s.to_i, s[/(?<=-)\d+/].to_i, s.gsub(/\A.+-/,'')] }
#=> ["1mo-20-extra", "1mo-30-classic", "1mo-30-super", "1mo-40-classic", "1mo-110-super",
# "6mo-11-super", "6mo-21-super", "12mo-21-classic", "12mo-21-super"]
When sorting arrays the method Arrays#<=> is used to order pairs of arrays. See the third paragraph of the doc for an explanation of how that is done.
The arrays used for the sort ordering are as follows.
array.each do |s|
puts "%-15s -> [%2d, %3d, %s]" % [s, s.to_i, s[/(?<=-)\d+/].to_i, s.gsub(/\A.+-/,'')]
end
1mo-30-super -> [ 1, 30, super]
1mo-40-classic -> [ 1, 40, classic]
1mo-30-classic -> [ 1, 30, classic]
1mo-110-super -> [ 1, 110, super]
1mo-20-extra -> [ 1, 20, extra]
6mo-21-super -> [ 6, 21, super]
6mo-11-super -> [ 6, 11, super]
12mo-21-classic -> [12, 21, classic]
12mo-21-super -> [12, 21, super]
(?<=-) is a positive lookbehind. It requires that the match be immediately preceded by a hyphen. /\A.+-/ matches the beginning of the string followed by one or more characters followed by a hyphen. Because regular expressions are by default greedy, it concludes the match on the second hyphen.
Note that it is not necessary to use regular expressions:
array.sort_by { |s| [s.to_i, s[s.index('-')+1..-1].to_i, s[s.rindex('-')+1..-1]] }