Class: DottedHash

Inherits:
Object
  • Object
show all
Extended by:
ActiveModel::Naming
Includes:
ActiveModel::Conversion
Defined in:
lib/dotted_hash.rb,
lib/dotted_hash/version.rb

Overview

See Readme.md in gem/repository directory for usage instructions

Constant Summary collapse

MAX_DEPTH =

Maximum depth of whole tree, not keys (keys depth+1). Counted from 0. Not fully bulletproof, depth may be set to wrong number if careless.

10
MAX_ATTRS =

Maximum count of attributes. Use hash like this to specify each level. MAX_ATTRS = => 20, 2 => 5, default: 10

10
MAX_SIZE =

Maximum size of document, counted from JSON result of document. It is not bulletproof, but if using simple structures, it is enough. Other structures may have much bigger representation in memory than in JSON.

16384
VERSION =
"1.0"

Instance Method Summary collapse

Constructor Details

#initialize(args = {}, level = 0) ⇒ DottedHash

Create new instance, recursively converting all Hashes to DottedHash and leaving everything else alone.

Raises:

  • (ArgumentError)


34
35
36
37
38
39
40
41
42
43
# File 'lib/dotted_hash.rb', line 34

def initialize(args={}, level=0)
	raise ArgumentError, "Please pass a Hash-like object" unless args.respond_to?(:each_pair)
	raise RuntimeError, "Maximal depth reached" if level > MAX_DEPTH

	@depth = level
	@attributes = {}
	args.each_pair do |key, value|
		assign_value(key, value)
	end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *arguments) ⇒ Object

Delegate method to a key in underlying hash, if present, otherwise return nil.



86
87
88
89
90
91
92
93
94
# File 'lib/dotted_hash.rb', line 86

def method_missing(method_name, *arguments)
	if method_name.to_s[-1] == '=' && arguments.size == 1
		attribute = method_name.to_s.chop
		value = arguments.first
		assign_value(attribute, value)
	else
		@attributes[method_name.to_sym]
	end
end

Instance Method Details

#[](key) ⇒ Object

Provides access to attribute. Use when you have spaces and other non a-z_ characters in attribute name.



130
131
132
# File 'lib/dotted_hash.rb', line 130

def [](key)
	@attributes[key.to_sym]
end

#as_json(options = nil) ⇒ Object

Returns (filtered) Ruby Hash with characters and objects only allowed in JSON.



171
172
173
174
# File 'lib/dotted_hash.rb', line 171

def as_json(options=nil)
	hash = to_hash
	hash.respond_to?(:with_indifferent_access) ? hash.with_indifferent_access.as_json(options) : hash.as_json(options)
end

#classObject

Let’s pretend we’re someone else in Rails



184
185
186
187
188
189
190
# File 'lib/dotted_hash.rb', line 184

def class
	begin
		defined?(::Rails) && @attributes[:_type] ? @attributes[:_type].camelize.constantize : super
	rescue NameError
		super
	end
end

#errorsObject

Standard ActiveModel Errors



145
146
147
# File 'lib/dotted_hash.rb', line 145

def errors
	ActiveModel::Errors.new(self)
end

#idObject

Returns id of document



136
137
138
# File 'lib/dotted_hash.rb', line 136

def id
	@attributes[:id]
end

#inspectObject



192
193
194
195
# File 'lib/dotted_hash.rb', line 192

def inspect
	s = []; @attributes.each { |k,v| s << "#{k}: #{v.inspect}" }
	%Q|<DottedHash#{self.class.to_s == 'DottedHash' ? '' : " (#{self.class})"} #{s.join(', ')}>|
end

#merge!(obj) ⇒ Object

Merge with another hash



46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/dotted_hash.rb', line 46

def merge!(obj)
	if obj.respond_to? :to_h
	  hash = obj.to_h
	elsif obj.respond_to? :to_hash
	  hash = obj.to_hash
	else
	  raise('Merge works only with hashlike object')
	end
	
	hash.each do |key, value|
		assign_value(key, value)
	end
	self
end

#persisted?Boolean

Returns:

  • (Boolean)


140
141
142
# File 'lib/dotted_hash.rb', line 140

def persisted?
	!!id
end

#recursive_assign(key, value) ⇒ Object

Recursively assigns value. Also creates sub-DottedHashes if they don’t exist).



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/dotted_hash.rb', line 111

def recursive_assign(key, value)
	return nil if key.blank?
	keys = key.split('.')
	if keys.size > 1
		key = keys.shift.to_sym

		if !@attributes[key]
			assign_value(key, DottedHash.new({}, @depth+1))
		end
		sub = @attributes[key]
		sub.send(:recursive_assign, keys.join('.'), value)
	elsif keys.size == 1
		assign_value(keys.shift, value)
	end
end

#respond_to?(method_name, include_private = false) ⇒ Boolean

Always respond to write. Respond to attribute or defined method.

Returns:

  • (Boolean)


99
100
101
102
103
104
105
106
# File 'lib/dotted_hash.rb', line 99

def respond_to?(method_name, include_private = false)
	# answers to any write method
	if method_name.to_s[-1] == '='
		true
	else
		@attributes.has_key?(method_name.to_sym) || super
	end
end

#to_hashObject Also known as: to_h



159
160
161
162
163
164
# File 'lib/dotted_hash.rb', line 159

def to_hash
	@attributes.reduce({}) do |sum, item|
		sum[ item.first ] = item.last.respond_to?(:to_hash) ? item.last.to_hash : item.last
		sum
	end
end

#to_json(options = nil) ⇒ Object Also known as: to_indexed_json

JSON string of as_json result



177
178
179
# File 'lib/dotted_hash.rb', line 177

def to_json(options=nil)
	as_json.to_json(options)
end

#to_keyObject

Returns key if key exists



155
156
157
# File 'lib/dotted_hash.rb', line 155

def to_key
	persisted? ? [id] : nil
end

#valid?Boolean

Always true

Returns:

  • (Boolean)


150
151
152
# File 'lib/dotted_hash.rb', line 150

def valid?
	true
end