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)



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

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

def self.from_xml(xml_data)
  return nil if xml_data.to_s.empty?
  
  parser = Nori.new
  tree = self.new(parser.parse(xml_data))
  tree.replace_values!(nil, '')

  return tree
end

.from_xml_path(xml_path) ⇒ Object



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

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

.from_yml_path(yml_path) ⇒ Object



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

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 *************************************************************************************



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

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

#children(name) ⇒ Object



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

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

#clone_treeObject



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

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

#compact!Object

Remove all key with a nil value



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

def compact!
  @hash = compact
end

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



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

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.



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

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)


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

def empty?
  @hash.empty?
end

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

Returns:

  • (Boolean)


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

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



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

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



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

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

#insert(path, content) ⇒ Object



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

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



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

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



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

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



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

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

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



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

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



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

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



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

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



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

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



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

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



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

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

#to_jsonObject



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

def to_json
  @hash.to_json
end

#to_yamlObject



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

def to_yaml
  self.keys_to_s!

  return @hash.ya2yaml
end