From 1170de531d358e287c6161458850a07f6521f0fd Mon Sep 17 00:00:00 2001 From: kwatch Date: Sat, 19 Nov 2016 10:19:34 +0900 Subject: [PATCH 7/9] feat(psych): define 'Psych::Visitors::CustomClassVisitor' class This new class allows user to generate custom object instead of Hash. See document of CustomClassVisitor class for details. --- ext/psych/lib/psych/visitors/custom_class.rb | 84 ++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 ext/psych/lib/psych/visitors/custom_class.rb diff --git a/ext/psych/lib/psych/visitors/custom_class.rb b/ext/psych/lib/psych/visitors/custom_class.rb new file mode 100644 index 0000000..313e522 --- /dev/null +++ b/ext/psych/lib/psych/visitors/custom_class.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: false + +require 'psych/visitors/to_ruby' + + +module Psych + module Visitors + + ## + ## Visitor class to generate custom object instead of Hash. + ## + ## Example1: + ## + ## ## define custom classes + ## Team = Struct.new('Team', 'name', 'members') + ## Member = Struct.new('Member', 'name', 'gender') + ## ## create visitor object + ## require 'psych' + ## require 'psych/visitors/custom_class' + ## classmap = { + ## "teams" => Team, + ## "members" => Member, + ## } + ## visitor = Psych::Visitors::CustomClassVisitor.create(classmap) + ## ## parse YAML document with custom classes + ## input = <<-'END' + ## teams: + ## - name: SOS Brigade + ## members: + ## - {name: Haruhi, gender: F} + ## - {name: Kyon, gender: M} + ## - {name: Mikuru, gender: F} + ## - {name: Itsuki, gender: M} + ## - {name: Yuki, gender: F} + ## END + ## tree = Psych.parse(input) + ## ydoc = visitor.accept(tree) + ## p ydoc['teams'][0].class #=> Struct::Team + ## p ydoc['teams'][0]['members'][0].class #=> Struct::Member + ## team = ydoc['teams'][0] + ## p team.name #=> "SOS Brigade" + ## p team.members[0].name #=> "Haruhi" + ## p team.members[0].gender #=> "F" + ## + class CustomClassVisitor < ToRuby + + def self.create(classmap={}) + visitor = super() + visitor.instance_variable_set('@classmap', classmap) + visitor + end + + attr_reader :classmap # key: string, value: class object + + def initialize(*args) + super + @key_path = [] # ex: [] -> ['tables'] -> ['tables', 'columns'] + end + + def accept_key(k) # push keys + key = super k + @key_path << key + key + end + + def accept_value(v) # pop keys + value = super v + @key_path.pop() + value + end + + def empty_mapping(o) # generate custom object (or Hash object) + klass = @classmap[@key_path.last] + klass ? klass.new : super + end + + def merge_mapping(hash, val) # for '<<' (merge) + val.each {|k, v| hash[k] = v } + end + + end + + end +end -- 2.9.3 (Apple Git-75)