module TidyJson

A mixin providing (recursive) JSON serialization and pretty printing.

Constants

VERSION

Public Class Methods

sort_keys(obj = {}) click to toggle source

Returns the given obj with keys in ascending order to a maximum depth of 2.

@param obj [Hash, Array<Hash>] A dictionary-like object or collection

thereof.

@return [Hash, Array<Hash>, Object] A copy of the given obj with top- and

second-level keys in ascending order, or else an identical copy of +obj+.

@note obj is returned unchanged if: 1) it's not iterable; 2) it's an

empty collection; 3) any one of its elements is not hashable (and +obj+
is an array).
# File lib/tidy_json.rb, line 60
def self.sort_keys(obj = {})
  return obj if !obj.respond_to?(:each) || obj.empty? ||
                (obj.instance_of?(Array) &&
                 !obj.all? { |e| e.respond_to? :keys })

  sorted = {}
  sorter = lambda { |data, ret_val|
    data.keys.sort.each do |k|
      ret_val[k.to_sym] = if data[k].instance_of? Hash
                            sorter.call(data[k], {})
                          else
                            data[k]
                          end
    end

    return ret_val
  }

  if obj.instance_of? Array
    temp = {}
    sorted = []

    (obj.sort_by { |h| h.keys.first }).each_with_index do |h, idx|
      temp[idx] = sorter.call(h, {})
    end

    temp.each_key { |k| sorted << temp[k] }
  else
    sorted = sorter.call(obj, {})
  end

  sorted
end
tidy(obj = {}, opts = {}) click to toggle source

Emits a pretty-printed JSON representation of the given obj.

@param obj [Object] A Ruby object that can be parsed as JSON. @param opts [Hash] Output format options. @option (see Formatter#initialize) @return [String] A pretty-printed JSON string.

# File lib/tidy_json.rb, line 19
def self.tidy(obj = {}, opts = {})
  formatter = Formatter.new(opts)
  json = ''

  begin
    if obj.instance_variables.empty?
      obj = sort_keys(obj) if formatter.format[:sorted]
      json = JSON.generate(obj, formatter.format)
    else
      str = "{\n"
      obj = JSON.parse(obj.stringify)
      obj = sort_keys(obj) if formatter.format[:sorted]

      obj.each do |k, v|
        str << formatter.format[:indent] << "\"#{k}\": "
        str << formatter.format_node(v, obj)
      end

      str << "}\n"
      json = JSON.generate(JSON.parse(formatter.trim(str)), formatter.format)
    end

    json.gsub(/[\n\r]{2,}/, "\n")
        .gsub(/\[\s+\]/, '[]')
        .gsub(/{\s+}/, '{}') << "\n"
  rescue JSON::JSONError => e
    warn "#{__FILE__}.#{__LINE__}: #{e.message}"
  end
end

Public Instance Methods

stringify() click to toggle source

Emits a JSON representation of the sender object's visible attributes.

@return [String] A raw JSON string.

# File lib/tidy_json.rb, line 108
def stringify
  json_hash = {}

  begin
    json_hash = Serializer.serialize(self, class: self.class.name)
  rescue JSON::JSONError => e
    warn "#{__FILE__}.#{__LINE__}: #{e.message}"
  end

  json_hash.to_json
end
to_tidy_json(opts = {}) click to toggle source

Like TidyJson::tidy, but callable by the sender object.

@param opts [Hash] Output format options. @option (see Formatter#initialize) @return [String] A pretty-printed JSON string.

# File lib/tidy_json.rb, line 100
def to_tidy_json(opts = {})
  TidyJson.tidy(self, opts)
end
write_json(out = " click to toggle source

Writes a JSON representation of the sender object to the file specified by out.

@param out [String] The destination filename. @param opts [Hash] Output format options. @option (see Formatter#initialize) @option opts [Boolean] :tidy (false) Whether or not the output should be

pretty-printed.

@return [String, nil] The path to the written output file, if successful.

# File lib/tidy_json.rb, line 130
def write_json(out = "#{self.class.name}_#{Time.now.to_i}",
               opts = { tidy: false })
  path = nil

  File.open("#{out}.json", 'w') do |f|
    path =
      f << if opts[:tidy] then to_tidy_json(opts)
           elsif instance_variables.empty? then to_json
           else stringify
           end
  end

  path&.path
rescue Errno::ENOENT, Errno::EACCES, IOError, RuntimeError, NoMethodError => e
  warn "#{__FILE__}.#{__LINE__}: #{e.message}"
end