utils.rb 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. require 'jsonpath'
  2. require 'cgi'
  3. module Utils
  4. def self.unindent(s)
  5. s = s.gsub(/\t/, ' ').chomp
  6. min = ((s.split("\n").find {|l| l !~ /^\s*$/ })[/^\s+/, 0] || "").length
  7. if min > 0
  8. s.gsub(/^#{" " * min}/, "")
  9. else
  10. s
  11. end
  12. end
  13. def self.pretty_print(struct, indent = true)
  14. output = JSON.pretty_generate(struct)
  15. if indent
  16. output.gsub(/\n/i, "\n ")
  17. else
  18. output
  19. end
  20. end
  21. def self.normalize_uri(uri)
  22. begin
  23. URI(uri)
  24. rescue URI::Error
  25. URI(uri.to_s.gsub(/[^\-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]+/) { |unsafe|
  26. unsafe.bytes.each_with_object(String.new) { |uc, s|
  27. s << sprintf('%%%02X', uc)
  28. }
  29. }.force_encoding(Encoding::US_ASCII))
  30. end
  31. end
  32. def self.interpolate_jsonpaths(value, data, options = {})
  33. if options[:leading_dollarsign_is_jsonpath] && value[0] == '$'
  34. Utils.values_at(data, value).first.to_s
  35. else
  36. value.gsub(/<[^>]+>/).each { |jsonpath|
  37. Utils.values_at(data, jsonpath[1..-2]).first.to_s
  38. }
  39. end
  40. end
  41. def self.recursively_interpolate_jsonpaths(struct, data, options = {})
  42. case struct
  43. when Hash
  44. struct.inject({}) {|memo, (key, value)| memo[key] = recursively_interpolate_jsonpaths(value, data, options); memo }
  45. when Array
  46. struct.map {|elem| recursively_interpolate_jsonpaths(elem, data, options) }
  47. when String
  48. interpolate_jsonpaths(struct, data, options)
  49. else
  50. struct
  51. end
  52. end
  53. def self.value_at(data, path)
  54. values_at(data, path).first
  55. end
  56. def self.values_at(data, path)
  57. if path =~ /\Aescape /
  58. path.gsub!(/\Aescape /, '')
  59. escape = true
  60. else
  61. escape = false
  62. end
  63. result = JsonPath.new(path, :allow_eval => ENV['ALLOW_JSONPATH_EVAL'] == "true").on(data.is_a?(String) ? data : data.to_json)
  64. if escape
  65. result.map {|r| CGI::escape r }
  66. else
  67. result
  68. end
  69. end
  70. # Output JSON that is ready for inclusion into HTML. If you simply use to_json on an object, the
  71. # presence of </script> in the valid JSON can break the page and allow XSS attacks.
  72. # Optionally, pass `:skip_safe => true` to not call html_safe on the output.
  73. def self.jsonify(thing, options = {})
  74. json = thing.to_json.gsub('</', '<\/')
  75. if !options[:skip_safe]
  76. json.html_safe
  77. else
  78. json
  79. end
  80. end
  81. def self.pretty_jsonify(thing)
  82. JSON.pretty_generate(thing).gsub('</', '<\/')
  83. end
  84. class TupleSorter
  85. class SortableTuple
  86. attr_reader :array
  87. # The <=> method will call orders[n] to determine if the nth element
  88. # should be compared in descending order.
  89. def initialize(array, orders = [])
  90. @array = array
  91. @orders = orders
  92. end
  93. def <=> other
  94. other = other.array
  95. @array.each_with_index do |e, i|
  96. o = other[i]
  97. case cmp = e <=> o || e.to_s <=> o.to_s
  98. when 0
  99. next
  100. else
  101. return @orders[i] ? -cmp : cmp
  102. end
  103. end
  104. 0
  105. end
  106. end
  107. class << self
  108. def sort!(array, orders = [])
  109. array.sort_by! do |e|
  110. SortableTuple.new(e, orders)
  111. end
  112. end
  113. end
  114. end
  115. def self.sort_tuples!(array, orders = [])
  116. TupleSorter.sort!(array, orders)
  117. end
  118. def self.parse_duration(string)
  119. return nil if string.blank?
  120. case string.strip
  121. when /\A(\d+)\.(\w+)\z/
  122. $1.to_i.send($2.to_s)
  123. when /\A(\d+)\z/
  124. $1.to_i
  125. else
  126. STDERR.puts "WARNING: Invalid duration format: '#{string.strip}'"
  127. nil
  128. end
  129. end
  130. def self.if_present(string, method)
  131. if string.present?
  132. string.send(method)
  133. else
  134. nil
  135. end
  136. end
  137. end