# # Author:: Tim Hinderliter () # Copyright:: Copyright (c) 2010 Opscode, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Wrapper class for interacting with JSON. require 'ffi_yajl' class Chef class JSONCompat 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 class < false # is required to turn it off. if opts[:create_additions].nil? || opts[:create_additions] map_to_rb_obj(obj) else obj end end # Look at an object that's a basic type (from json parse) and convert it # to an instance of Chef classes if desired. 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 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 def to_json(obj, opts = nil) ::FFI_Yajl::Encoder.encode(obj, opts) end def to_json_pretty(obj, opts = nil) opts ||= {} options_map = {} options_map[:pretty] = true options_map[:indent] = opts[:indent] if opts.has_key?(:indent) ::FFI_Yajl::Encoder.encode(obj, options_map).chomp end # 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. 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::Resource/ Chef::Resource.find_subclass_by_name(json_class) else raise JSON::ParserError, "Unsupported `json_class` type '#{json_class}'" end end end end end