Class: BBOpenStruct

Inherits:
Object
  • Object
show all
Defined in:
lib/bb_openstruct.rb

Constant Summary collapse

InspectKey =

:nodoc:

:__inspect_key__

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hash = {}) ⇒ BBOpenStruct

Returns a new instance of BBOpenStruct.



9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/bb_openstruct.rb', line 9

def initialize(hash={})
  @binding = BBOpenStruct.pure_binding
  hash.each_pair do |k,v|
    @binding.local_variable_set(k.to_sym, v)
  end
  @binding.local_variables.each do |name|
    get = ->() { @binding.local_variable_get(name) }
    set = ->(new_var) { @binding.local_variable_set(name, new_var) }
    define_singleton_method(name, get)
    define_singleton_method("#{name}=".to_sym, set)
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, arg = nil) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/bb_openstruct.rb', line 49

def method_missing(name, arg=nil)
  name = name.to_sym
  if @binding.local_variables.include?(name)
    @binding.local_variable_get(name)
  elsif name.to_s[-1] == '=' && frozen?
    raise RuntimeError.new "can't modify frozen object"
  elsif name.to_s[-1] == '='
    pure_name = name.to_s.delete('=').to_sym
    @binding.local_variable_set(pure_name, arg)
    unless methods.include?(pure_name)
      set = ->(new_var) { @binding.local_variable_set(pure_name, new_var) }
      get = ->() { @binding.local_variable_get(pure_name) }
      define_singleton_method(name, set)
      define_singleton_method(pure_name, get)
    end
  else
    err = NoMethodError.new "undefined method `#{name}' for #{self}", name, [arg]
    err.set_backtrace caller(1)
    raise err
  end
end

Class Method Details

.pure_bindingObject



22
23
24
# File 'lib/bb_openstruct.rb', line 22

def self.pure_binding
  binding
end

Instance Method Details

#==(other) ⇒ Object



133
134
135
136
# File 'lib/bb_openstruct.rb', line 133

def ==(other)
  return false unless other.kind_of?(BBOpenStruct)
  to_h == other.to_h
end

#[](name) ⇒ Object



71
72
73
# File 'lib/bb_openstruct.rb', line 71

def [](name)
  method_missing(name)
end

#[]=(name, val) ⇒ Object



75
76
77
# File 'lib/bb_openstruct.rb', line 75

def []=(name, val)
  method_missing("#{name.to_sym}=", val)
end

#delete_field(name) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/bb_openstruct.rb', line 26

def delete_field(name)
  name = name.to_sym
  binding_old = @binding.dup
  @binding = BBOpenStruct.pure_binding
  binding_old.local_variables.each do |var|
    next if var == name
    @binding.local_variable_set(var, binding_old.local_variable_get(var))
  end

  instance_eval("undef #{name}=")
  instance_eval("undef #{name}")

  binding_old.local_variable_get(name)
end

#dig(name, *names) ⇒ Object

Retrieves the value object corresponding to the each name objects repeatedly.

address = BBOpenStruct.new('city' => "Anytown NC", 'zip' => 12345)
person = BBOpenStruct.new('name' => 'John Smith', 'address' => address)
person.dig(:address, 'zip') # => 12345
person.dig(:business_address, 'zip') # => nil


158
159
160
161
162
163
164
165
# File 'lib/bb_openstruct.rb', line 158

def dig(name, *names)
  begin
    name = name.to_sym
  rescue NoMethodError
    raise TypeError, "#{name} is not a symbol nor a string"
  end
  to_h.dig(name, *names)
end

#each_pairObject



115
116
117
118
119
120
121
122
# File 'lib/bb_openstruct.rb', line 115

def each_pair
  table = {}
  @binding.local_variables.each do |k|
    table[k] = @binding.local_variable_get(k)
  end
  return to_enum(__method__) { table.size } unless block_given?
  table.each_pair{|p| yield p}
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


138
139
140
141
# File 'lib/bb_openstruct.rb', line 138

def eql?(other)
  return false unless other.kind_of?(BBOpenStruct)
  to_h.eql?(other.to_h)
end

#freezeObject



41
42
43
44
45
46
47
# File 'lib/bb_openstruct.rb', line 41

def freeze
  singleton_methods.each do |method|
    instance_eval("undef #{method}") if method.to_s[-1] == '='
  end
  eval("def self.frozen?; true end")
  super
end

#hashObject

Compute a hash-code for this OpenStruct. Two hashes with the same content will have the same hash code (and will be eql?).



146
147
148
# File 'lib/bb_openstruct.rb', line 146

def hash
  to_h.hash
end

#inspectObject Also known as: to_s



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/bb_openstruct.rb', line 91

def inspect
  str = "#<#{self.class}"

  ids = (Thread.current[InspectKey] ||= [])
  if ids.include?(object_id)
    return str << ' ...>'
  end

  ids << object_id
  begin
    first = true
    @binding.local_variables.sort.each do |k|
      v = @binding.local_variable_get(k)
      str << "," unless first
      first = false
      str << " #{k}=#{v.inspect}"
    end
    return str << '>'
  ensure
    ids.pop
  end
end

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

Returns:

  • (Boolean)


79
80
81
82
83
84
85
86
87
88
# File 'lib/bb_openstruct.rb', line 79

def respond_to_missing?(method_name, include_private = false)
  last_char_name = method_name.to_s[-1]
  if last_char_name == '=' && frozen?
    false
  elsif last_char_name == '=' && @binding.local_variable_defined?(method_name.to_s.tr('=', ''))
    true
  else
    (@binding.local_variables.include?(method_name) && !@binding.local_variable_get(method_name)) || super
  end
end

#to_hObject



124
125
126
127
128
129
130
131
# File 'lib/bb_openstruct.rb', line 124

def to_h
  output = {}
  @binding.local_variables.each do |var|
    next if var == :hash
    output[var] = @binding.local_variable_get(var)
  end
  output
end