Module: Hashstructor::InstanceMethods

Included in:
Hashstructor
Defined in:
lib/hashstructor/instance_methods.rb

Overview

Instance methods for Hashstructor objects.

Instance Method Summary collapse

Instance Method Details

#hashstruct(hash) ⇒ Object (private)

Constructs the object from the given hash, based on the metadata fed in by ClassMethods#member invocations.

Parameters:

  • hash (Hash)

    the hash from which to parse the object data

Raises:



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/hashstructor/instance_methods.rb', line 71

def hashstruct(hash)
  raise HashstructorError, "hashstruct expects a hash." unless hash.is_a?(Hash)

  members = self.class.hashstructor_members

  # This could be done inline, but I'd rather loop twice and get this check
  # out of the way ahead of time because of the perf hit when dealing with
  # deeply nested structures.
  partitioned_members = members.partition { |m| !hash[m.name].nil? || !hash[m.name.to_s].nil? }

  existing_members = partitioned_members[0]
  missing_members = partitioned_members[1]

  partitioned_missing_members = missing_members.partition { |m| !m.required || (m.required && !m.options[:default_value].nil?) }

  acceptable_missing_members = partitioned_missing_members[0]
  unacceptable_missing_members = partitioned_missing_members[1]

  raise HashstructorError, "Missing required members: #{unacceptable_missing_members.map { |m| m.name }.join(", ")}" \
    unless unacceptable_missing_members.empty?

  acceptable_missing_members.each do |member|
    out_value = case member.member_type
                  when :normal
                    member.options[:default_value]
                  when :array
                    []
                  when :set
                    Set.new
                  when :hash
                    {}
                end

    instance_variable_set("@#{member.name}", out_value)
  end

  existing_members.each do |member|
    raw_value = hash[member.name]
    if raw_value.nil?
      raw_value = hash[member.name.to_s]
    end
    out_value = nil

    case member.member_type
      when :normal
        raise HashstructorError, "member '#{member.name}' expects a single value, got #{raw_value.class.name}." \
          if member.options[:no_collections] && raw_value.is_a?(Enumerable)

        out_value = member.parse_single(raw_value)

      when :hash
        raise HashstructorError, "member '#{member.name}' expects a Hash, got #{raw_value.class.name}." \
          unless raw_value.is_a?(Hash)

        out_value = {}
        raw_value.each_pair do |k, v|
          key = member.options[:string_keys] ? k.to_s : k.to_s.to_sym

          out_value[key] = member.parse_single(v)
        end

      when :array, :set
        raise HashstructorError, "member '#{member.name}' expects an Enumerable, got #{raw_value.class.name}"\
          unless raw_value.is_a?(Enumerable)

        out_value = raw_value.map { |v| member.parse_single(v) }

        out_value = out_value.to_set if member.member_type == :set
    end

    instance_variable_set("@#{member.name}", out_value)
  end
end

#initialize(hash) ⇒ Object (private)

Initializes the object. This exists for objects that prepend Hashstructor; objects that include it must explicitly invoke #hashstruct within their own initialize method.

Parameters:

  • hash (Hash)

    the hash from which to parse the object data



63
64
65
# File 'lib/hashstructor/instance_methods.rb', line 63

def initialize(hash)
  hashstruct(hash)
end

#to_hObject

Converts the class back to a hash.



11
12
13
# File 'lib/hashstructor/instance_methods.rb', line 11

def to_h
  to_hash
end

#to_hashObject

Converts the class back to a hash.



18
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
48
49
50
51
52
53
54
55
# File 'lib/hashstructor/instance_methods.rb', line 18

def to_hash
  hash = {}

  self.class.hashstructor_members.each do |member|
    member_value = instance_variable_get("@#{member.name}")

    out_value = 
      case member.member_type
        when :normal
          member_value.nil? ? nil : member.to_hash_value(member_value)
        when :array
          # There's some weird Ruby thing I don't get here, but if I use
          # a #select instead of an each + container, I end up returning
          # the actual objects rather than their to_hashes.
          container = []
          member_value.each do |v|
            container << member.to_hash_value(v) 
          end
          container
        when :set
          container = Set.new
          member_value.each do |v|
            container << member.to_hash_value(v) 
          end
          container
        when :hash
          container = {}
          member_value.each do |k, v| 
            container[k] = member.to_hash_value(v)
          end
          container
      end

    hash[member.name.to_sym] = out_value
  end

  hash
end