Class: Chef::JSONCompat

Inherits:
Object
  • Object
show all
Defined in:
lib/chef/json_compat.rb

Constant Summary collapse

JSON_MAX_NESTING =
1000
JSON_CLASS =
"json_class".freeze
CHEF_APICLIENT =
"Chef::ApiClient".freeze
CHEF_CHECKSUM =
"Chef::Checksum".freeze
CHEF_COOKBOOKVERSION =
"Chef::CookbookVersion".freeze
CHEF_DATABAG =
"Chef::DataBag".freeze
CHEF_DATABAGITEM =
"Chef::DataBagItem".freeze
CHEF_ENVIRONMENT =
"Chef::Environment".freeze
CHEF_NODE =
"Chef::Node".freeze
CHEF_ROLE =
"Chef::Role".freeze
CHEF_SANDBOX =
"Chef::Sandbox".freeze
CHEF_RESOURCE =
"Chef::Resource".freeze
CHEF_RESOURCECOLLECTION =
"Chef::ResourceCollection".freeze
CHEF_RESOURCESET =
"Chef::ResourceCollection::ResourceSet".freeze
CHEF_RESOURCELIST =
"Chef::ResourceCollection::ResourceList".freeze
CHEF_RUNLISTEXPANSION =
"Chef::RunListExpansion".freeze

Class Method Summary collapse

Class Method Details

.class_for_json_class(json_class) ⇒ Object

Map json_class to a Class object. We use a case instead of a Hash assigned to a constant because otherwise this file could not be loaded until all the constants were defined, which means you’d have to load the world to get json, which would make knife very slow.



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/chef/json_compat.rb', line 124

def class_for_json_class(json_class)
  case json_class
  when CHEF_APICLIENT
    Chef::ApiClient
  when CHEF_CHECKSUM
    Chef::Checksum
  when CHEF_COOKBOOKVERSION
    Chef::CookbookVersion
  when CHEF_DATABAG
    Chef::DataBag
  when CHEF_DATABAGITEM
    Chef::DataBagItem
  when CHEF_ENVIRONMENT
    Chef::Environment
  when CHEF_NODE
    Chef::Node
  when CHEF_ROLE
    Chef::Role
  when CHEF_SANDBOX
    # a falsey return here will disable object inflation/"create
    # additions" in the caller. In Chef 11 this is correct, we just have
    # a dummy Chef::Sandbox class for compat with Chef 10 servers.
    false
  when CHEF_RESOURCE
    Chef::Resource
  when CHEF_RESOURCECOLLECTION
    Chef::ResourceCollection
  when CHEF_RESOURCESET
    Chef::ResourceCollection::ResourceSet
  when CHEF_RESOURCELIST
    Chef::ResourceCollection::ResourceList
  when /^Chef::Resource/
    Chef::Resource.find_descendants_by_name(json_class)
  else
    raise Chef::Exceptions::JSON::ParseError, "Unsupported `json_class` type '#{json_class}'"
  end
end

.from_json(source, opts = {}) ⇒ Object

Just call the JSON gem’s parse method with a modified :max_nesting field



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/chef/json_compat.rb', line 58

def from_json(source, opts = {})
  obj = parse(source, opts)

  # JSON gem requires top level object to be a Hash or Array (otherwise
  # you get the "must contain two octets" error). Yajl doesn't impose the
  # same limitation. For compatibility, we re-impose this condition.
  unless obj.kind_of?(Hash) || obj.kind_of?(Array)
    raise Chef::Exceptions::JSON::ParseError, "Top level JSON object must be a Hash or Array. (actual: #{obj.class})"
  end

  # The old default in the json gem (which we are mimicing because we
  # sadly rely on this misfeature) is to "create additions" i.e., convert
  # JSON objects into ruby objects. Explicit :create_additions => false
  # is required to turn it off.
  if opts[:create_additions].nil? || opts[:create_additions]
    map_to_rb_obj(obj)
  else
    obj
  end
end

.map_hash_to_rb_obj(json_hash) ⇒ Object



97
98
99
100
101
102
# File 'lib/chef/json_compat.rb', line 97

def map_hash_to_rb_obj(json_hash)
  json_hash.each do |key, value|
    json_hash[key] = map_to_rb_obj(value)
  end
  json_hash
end

.map_to_rb_obj(json_obj) ⇒ Object

Look at an object that’s a basic type (from json parse) and convert it to an instance of Chef classes if desired.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/chef/json_compat.rb', line 81

def map_to_rb_obj(json_obj)
  case json_obj
  when Hash
    mapped_hash = map_hash_to_rb_obj(json_obj)
    if json_obj.has_key?(JSON_CLASS) && (class_to_inflate = class_for_json_class(json_obj[JSON_CLASS]))
      class_to_inflate.json_create(mapped_hash)
    else
      mapped_hash
    end
  when Array
    json_obj.map { |e| map_to_rb_obj(e) }
  else
    json_obj
  end
end

.parse(source, opts = {}) ⇒ Object

API to use to avoid create_addtions



49
50
51
52
53
54
55
# File 'lib/chef/json_compat.rb', line 49

def parse(source, opts = {})
  begin
    FFI_Yajl::Parser.parse(source, opts)
  rescue FFI_Yajl::ParseError => e
    raise Chef::Exceptions::JSON::ParseError, e.message
  end
end

.to_json(obj, opts = nil) ⇒ Object



104
105
106
107
108
109
110
# File 'lib/chef/json_compat.rb', line 104

def to_json(obj, opts = nil)
  begin
    FFI_Yajl::Encoder.encode(obj, opts)
  rescue FFI_Yajl::EncodeError => e
    raise Chef::Exceptions::JSON::EncodeError, e.message
  end
end

.to_json_pretty(obj, opts = nil) ⇒ Object



112
113
114
115
116
117
118
# File 'lib/chef/json_compat.rb', line 112

def to_json_pretty(obj, opts = nil)
  opts ||= {}
  options_map = {}
  options_map[:pretty] = true
  options_map[:indent] = opts[:indent] if opts.has_key?(:indent)
  to_json(obj, options_map).chomp
end