Module: TidyJson

Included in:
Object
Defined in:
lib/tidy_json.rb,
lib/tidy_json/version.rb,
lib/tidy_json/formatter.rb,
lib/tidy_json/serializer.rb

Overview

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

Defined Under Namespace

Classes: Formatter, Serializer

Constant Summary collapse

VERSION =
'0.5.2'

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.sort_keys(obj = {}) ⇒ Hash, ...

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).

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

Parameters:

  • obj (Hash, Array<Hash>) (defaults to: {})

    A dictionary-like object or collection thereof.

Returns:

  • (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.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# 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 = {}) ⇒ String

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

Parameters:

  • obj (Object) (defaults to: {})

    A Ruby object that can be parsed as JSON.

  • opts (Hash) (defaults to: {})

    Output format options.

Options Hash (opts):

  • :indent ([2,4,6,8,10,12]) — default: 2

    The number of spaces to indent each object member.

  • :space_before ([1..8]) — default: 0

    The number of spaces to put after property names.

  • :space ([1..8]) — default: 1

    The number of spaces to put before property values.

  • :object_nl (String) — default: "\n"

    A string of whitespace to delimit object members.

  • :array_nl (String) — default: "\n"

    A string of whitespace to delimit array elements.

  • :max_nesting (Numeric) — default: 100

    The maximum level of data structure nesting in the generated JSON. Disable depth checking by passing max_nesting: 0.

  • :escape_slash (Boolean) — default: false

    Whether or not a forward slash (/) should be escaped.

  • :ascii_only (Boolean) — default: false

    Whether or not only ASCII characters should be generated.

  • :allow_nan (Boolean) — default: false

    Whether or not to allow NaN, Infinity and -Infinity. If false, an exception is thrown if one of these values is encountered.

  • :sort (Boolean) — default: false

    Whether or not object members should be sorted by property name.

Returns:

  • (String)

    A pretty-printed JSON string.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 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

Instance Method Details

#stringifyString

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

Returns:

  • (String)

    A raw JSON string.



108
109
110
111
112
113
114
115
116
117
118
# 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 = {}) ⇒ String

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

Parameters:

  • opts (Hash) (defaults to: {})

    Output format options.

Options Hash (opts):

  • :indent ([2,4,6,8,10,12]) — default: 2

    The number of spaces to indent each object member.

  • :space_before ([1..8]) — default: 0

    The number of spaces to put after property names.

  • :space ([1..8]) — default: 1

    The number of spaces to put before property values.

  • :object_nl (String) — default: "\n"

    A string of whitespace to delimit object members.

  • :array_nl (String) — default: "\n"

    A string of whitespace to delimit array elements.

  • :max_nesting (Numeric) — default: 100

    The maximum level of data structure nesting in the generated JSON. Disable depth checking by passing max_nesting: 0.

  • :escape_slash (Boolean) — default: false

    Whether or not a forward slash (/) should be escaped.

  • :ascii_only (Boolean) — default: false

    Whether or not only ASCII characters should be generated.

  • :allow_nan (Boolean) — default: false

    Whether or not to allow NaN, Infinity and -Infinity. If false, an exception is thrown if one of these values is encountered.

  • :sort (Boolean) — default: false

    Whether or not object members should be sorted by property name.

Returns:

  • (String)

    A pretty-printed JSON string.



100
101
102
# File 'lib/tidy_json.rb', line 100

def to_tidy_json(opts = {})
  TidyJson.tidy(self, opts)
end

#write_json(out = "#{self.class.name}_#{Time.now.to_i}", opts = { tidy: false }) ⇒ String?

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

Parameters:

  • out (String) (defaults to: "#{self.class.name}_#{Time.now.to_i}")

    The destination filename.

  • opts (Hash) (defaults to: { tidy: false })

    Output format options.

Options Hash (opts):

  • :tidy (Boolean) — default: false

    Whether or not the output should be pretty-printed.

  • :indent ([2,4,6,8,10,12]) — default: 2

    The number of spaces to indent each object member.

  • :space_before ([1..8]) — default: 0

    The number of spaces to put after property names.

  • :space ([1..8]) — default: 1

    The number of spaces to put before property values.

  • :object_nl (String) — default: "\n"

    A string of whitespace to delimit object members.

  • :array_nl (String) — default: "\n"

    A string of whitespace to delimit array elements.

  • :max_nesting (Numeric) — default: 100

    The maximum level of data structure nesting in the generated JSON. Disable depth checking by passing max_nesting: 0.

  • :escape_slash (Boolean) — default: false

    Whether or not a forward slash (/) should be escaped.

  • :ascii_only (Boolean) — default: false

    Whether or not only ASCII characters should be generated.

  • :allow_nan (Boolean) — default: false

    Whether or not to allow NaN, Infinity and -Infinity. If false, an exception is thrown if one of these values is encountered.

  • :sort (Boolean) — default: false

    Whether or not object members should be sorted by property name.

Returns:

  • (String, nil)

    The path to the written output file, if successful.



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# 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