Class: HashTree

Inherits:
Object
  • Object
show all
Defined in:
lib/hash-tree.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hash = {}) ⇒ HashTree

************************************************************************************* CONSTRUCTOR *************************************************************************************



8
9
10
11
12
# File 'lib/hash-tree.rb', line 8

def initialize(hash={})
  hash = {} unless hash.is_a? Hash

  @hash = hash
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(m, *args, &block) ⇒ Object (private)



474
475
476
477
478
479
480
481
# File 'lib/hash-tree.rb', line 474

def method_missing(m, *args, &block)
  if exists?(m.to_s)
    return get(m.to_s)
  else
    return nil
    #raise "DIG : The method #{m} doesn't exist in HashTree"
  end
end

Class Method Details

.from_json(json_data) ⇒ Object

************************************************************************************* PUBLIC CLASS METHODS *************************************************************************************



18
19
20
21
22
23
24
25
26
27
# File 'lib/hash-tree.rb', line 18

def self.from_json(json_data)
  return nil if json_data.to_s.empty?

  parsed_data = JSON.parse(json_data)

  tree = self.new(parsed_data)
  tree.replace_values!(nil, '')

  return tree
end

.from_json_path(json_path) ⇒ Object



29
30
31
# File 'lib/hash-tree.rb', line 29

def self.from_json_path(json_path)
  from_json File.read(json_path)
end

.from_xml(xml_data) ⇒ Object



33
34
35
36
37
38
39
40
# File 'lib/hash-tree.rb', line 33

def self.from_xml(xml_data)
  return nil if xml_data.to_s.empty?

  tree = self.new(Nori.parse(xml_data))
  tree.replace_values!(nil, '')

  return tree
end

.from_xml_path(xml_path) ⇒ Object



42
43
44
# File 'lib/hash-tree.rb', line 42

def self.from_xml_path(xml_path)
  from_xml File.read(xml_path)
end

.from_yml_path(yml_path) ⇒ Object



46
47
48
49
50
51
52
# File 'lib/hash-tree.rb', line 46

def self.from_yml_path(yml_path)
  yml_data = YAML.load_file(yml_path)

  tree = self.new(yml_data)

  return tree
end

Instance Method Details

#checksumObject

************************************************************************************* PUBLIC METHODS *************************************************************************************



58
59
60
# File 'lib/hash-tree.rb', line 58

def checksum
  Digest::MD5.hexdigest(@hash.to_json.scan(/\S/).sort.join)
end

#children(name) ⇒ Object



62
63
64
65
66
67
68
# File 'lib/hash-tree.rb', line 62

def children(name)
  if @hash[name] and @hash[name][name.chop]
    return [@hash[name][name.chop]].flatten
  else
    return []
  end
end

#clone_treeObject



70
71
72
# File 'lib/hash-tree.rb', line 70

def clone_tree
  HashTree.new(Marshal.load(Marshal.dump(@hash)))
end

#compact!Object

Remove all key with a nil value



75
76
77
# File 'lib/hash-tree.rb', line 75

def compact!
  @hash = compact
end

#each(options = {}, &block) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/hash-tree.rb', line 79

def each(options={}, &block)
  options = { :hash => @hash, :key_path => [], :scope => nil }.merge(options)

  options[:hash].each do |key, value|
    key_path = [options[:key_path], key].flatten
    key_path_string = key_path.join('.')

    if in_scope?(key_path_string, options[:scope])
      if (options[:scope] and options[:scope] == key_path_string)
        yield options[:hash], key, value, key_path_string
      else
        cast(value, Array).each do |item|
          if item.is_a? Hash
            each(:hash => item, :key_path => key_path, :scope => options[:scope], &block)
          else
            yield options[:hash], key, item, key_path_string
          end
        end
      end
    end
  end
end

#each_node(path, options = {}, &block) ⇒ Object

Takes a path (keys separated by dots) and yield an hash of the parents of all level (ascendants) up to the root, along with the value of the path.



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
# File 'lib/hash-tree.rb', line 103

def each_node(path, options={}, &block)
  options = { :hash => @hash, :key_path => [], :parents => {} }.merge(options)
  return unless path && !path.empty?

  all_keys = path.split('.')
  keys = all_keys[options[:key_path].size..-1]
  key = keys.shift
  key_path = [options[:key_path], key].flatten
  key_path_string = key_path.join('.')
  value = options[:hash][key]

  if value
    parents = options[:parents].merge(Hash[key_path_string, options[:hash]])

    # Go no further?
    if (key_path == all_keys)
      yield parents, value
    else
      if value.kind_of? Array
        value.each do |item|
          if item.kind_of? Hash
            each_node(path, {:hash => item, :key_path => key_path, :parents => parents}, &block)
          end
        end
      elsif value.kind_of? Hash
        each_node(path, {:hash => value, :key_path => key_path, :parents => parents}, &block)
      end
    end
  end
end

#empty?Boolean

Returns:

  • (Boolean)


134
135
136
# File 'lib/hash-tree.rb', line 134

def empty?
  @hash.empty?
end

#exists?(path = '', hash = nil) ⇒ Boolean

Returns:

  • (Boolean)


138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/hash-tree.rb', line 138

def exists?(path='', hash=nil)
  hash = @hash unless hash

  path_parts = path.split('.')

  hash.each do |key, value|
    value_for_loop = (value.is_a? Array) ? value : [value]

    if path_parts[0] == key
      return true if path_parts.length == 1

      value_for_loop.each do |item|
        if item.is_a?(Hash)
          return true if exists?(path_parts[1..-1].join('.'), item)
        end
      end

    end
  end

  return false
end

#get(path = '', options = {}) ⇒ Object



161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/hash-tree.rb', line 161

def get(path='', options={})
  options = { :default => '', :force => nil }.merge(options)

  if not path.empty?
    data = []
    self.each(:scope => path) { |parent, k, v| data << cast(v, options[:force]) }
    data = (data.length <= 1 ? data.first : data)
  else
    data = @hash
  end

  return (data == nil ? options[:default] : data)
end

#idObject



188
189
190
191
# File 'lib/hash-tree.rb', line 188

def id
  #override the id method of all object
  get('id')
end

#insert(path, content) ⇒ Object



193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/hash-tree.rb', line 193

def insert(path, content)
  current_value = get(path)

  if current_value.is_a? Array
    if content.is_a? Array
      current_value = current_value.concat(content)
    else
      current_value << content
    end
  end

  set(path, current_value)
end

#inspect(options = {}) ⇒ Object



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/hash-tree.rb', line 207

def inspect(options={})
  options = { :hash => nil, :position => 0, :raw => true }.merge(options)

  options[:hash] = @hash unless options[:hash]

  return options[:hash].inspect if options[:raw]

  content = ""

  options[:hash].each do |key, value|
    convert_to_array(value).each do |item|
      content << "\n" + ('   ' * options[:position]) + "#{key} : "
      content << (item.is_a?(Hash) ? inspect(:hash => item, :position => options[:position]+1) : value.inspect)
    end
  end

  return content
end

#keys_to_s!(options = {}) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/hash-tree.rb', line 175

def keys_to_s!(options={})
  options = { :hash => nil }.merge(options)

  options[:hash] = @hash unless options[:hash]

  options[:hash].keys_to_s!

  options[:hash].each do |key, value|
    value_for_loop = (value.is_a? Array) ? value : [value]
    value_for_loop.each { |item| keys_to_s!(:hash => item) if item.is_a? Hash }
  end
end

#merge(other_hash) ⇒ Object



226
227
228
# File 'lib/hash-tree.rb', line 226

def merge(other_hash)
  @hash = merge_children(@hash, other_hash)
end

#remove(path, options = {}) ⇒ Object



230
231
232
233
234
# File 'lib/hash-tree.rb', line 230

def remove(path, options={})
  options = { :if => nil, :remove_leaf => true, :unless => nil }.merge(options)

  set(path, nil, options) if exists?(path)
end

#rename_key!(path, new_name, hash = nil) ⇒ Object



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/hash-tree.rb', line 236

def rename_key!(path, new_name, hash=nil)
  hash = @hash unless hash

  path_parts = path.split('.')

  renamed_keys = {}

  hash.each do |key, value|
    if path_parts[0] == key
      if path_parts.length == 1
        renamed_keys[new_name] = hash.delete path_parts[0]
      else
        convert_to_array(value).each { |i| rename_key!(path_parts[1..-1].join('.'), new_name, i) if i.is_a? Hash }
      end
    end
  end

  hash.merge! renamed_keys if not renamed_keys.empty?
end

#replace_values!(old_value, new_value, hash = nil) ⇒ Object



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/hash-tree.rb', line 256

def replace_values!(old_value, new_value, hash=nil)
  hash = @hash unless hash

  hash.each do |key, value|
    if value.is_a? Array
      value.each do |item|
        if item.is_a?(Hash)
          item = replace_values!(old_value, new_value, item)
        else
          item = new_value if item == old_value
        end
      end
    elsif value.is_a? Hash
      value = replace_values!(old_value, new_value, value)
    elsif value == old_value
      value = new_value
    end

    hash[key] = value
  end

  return hash
end

#set(path, value, options = {}) ⇒ Object



280
281
282
283
284
# File 'lib/hash-tree.rb', line 280

def set(path, value, options={})
  options = { :accept_nil => true, :if => nil, :remove_leaf => false, :unless => nil }.merge(options)

  set_children(@hash, path, value, options) if options[:accept_nil] or value
end

#slash(path) ⇒ Object



286
287
288
289
290
291
292
293
294
295
# File 'lib/hash-tree.rb', line 286

def slash(path)
  if exists?(path)
    slashed_tree = get(path)
    slashed_tree = slashed_tree.first if slashed_tree.is_a? Array and slashed_tree.length == 1 and slashed_tree.first.is_a? Hash
  else
    slashed_tree = @hash
  end

  return HashTree.new(slashed_tree)
end

#slash!(path) ⇒ Object



297
298
299
# File 'lib/hash-tree.rb', line 297

def slash!(path)
  @hash = slash(path).get
end

#to_jsonObject



301
302
303
# File 'lib/hash-tree.rb', line 301

def to_json
  @hash.to_json
end

#to_yamlObject



305
306
307
308
309
# File 'lib/hash-tree.rb', line 305

def to_yaml
  self.keys_to_s!

  return @hash.ya2yaml
end