aetherknight / recursive-open-struct

OpenStruct subclass that returns nested hash attributes as RecursiveOpenStructs
Other
276 stars 54 forks source link

[help] Need help with this issue #66

Closed jvalrog closed 4 years ago

jvalrog commented 4 years ago

Hello, thanks for this gem, it's great. I'm trying this code but for some reason it doesn't work:

require "yaml"
require "recursive-open-struct"

class Config < RecursiveOpenStruct
    def initialize(filename)
        @filename = filename
        super(YAML.load_file(@filename), {recurse_over_arrays: true, preserve_original_keys: true})
    end
end

config = Config.new("config.yml")
puts config
puts config.devices

The result is this:

$ ruby config.rb
#<Config devices={"lan"=>"eth0", "wlan"=>"wlan0", "wan"=>"wan0", "bridge"=>"br0"}, lan={"address"=>"192.168.1.1", "dhcp"=>{"enabled"=>true, "start_host"=>100, "hosts"=>50, "lease"=>5, "dns"=>["1.1.1.1", "8.8.8.8"]}, "ap"=>{"enabled"=>true, "ssid"=>"", "password"=>"", "channel"=>5, "hidden"=>false, "isolated"=>false}}, wan={"setup"=>"static", "static"=>{"address"=>"172.26.0.7", "netmask"=>"255.255.255.0", "gateway"=>"172.26.0.1"}, "pppoe"=>{"user"=>"", "password"=>""}}, router={"mode"=>"router", "hostname"=>"raspberrypi", "login"=>{"username"=>"", "password"=>""}, "ntp"=>{"enabled"=>true, "zone"=>""}, "dmz_host"=>3, "dns"=>["1.0.0.1", "8.8.4.4"]}, routes=[{"network"=>"10.0.0.0", "netmask"=>"255.255.255.0", "host"=>8}, {"network"=>"10.0.1.0", "netmask"=>"255.255.255.0", "host"=>9}], port_forwarding=[{"name"=>"web", "host"=>6, "proto"=>"tcp", "ports"=>"80,443"}, {"name"=>"ssh", "host"=>7, "proto"=>"tcp", "ports"=>"22"}]>
Traceback (most recent call last):
    5: from config.rb:12:in `<main>'
    4: from /var/lib/gems/2.5.0/gems/recursive-open-struct-1.1.1/lib/recursive_open_struct.rb:104:in `method_missing'
    3: from /var/lib/gems/2.5.0/gems/recursive-open-struct-1.1.1/lib/recursive_open_struct.rb:120:in `block (2 levels) in new_ostruct_member'
    2: from /var/lib/gems/2.5.0/gems/recursive-open-struct-1.1.1/lib/recursive_open_struct.rb:59:in `[]'
    1: from /var/lib/gems/2.5.0/gems/recursive-open-struct-1.1.1/lib/recursive_open_struct.rb:59:in `new'
config.rb:5:in `initialize': wrong number of arguments (given 2, expected 1) (ArgumentError)

If I use OpenStruct instead of RecursiveOpenStruct, the code works (but it's not recursive).

Apparently, the first "puts" works, but not the other one. Am I missing something? It's been a while from my coder days.

Thanks

aetherknight commented 4 years ago

When you subclass ROS, it uses your subclass for the children instances. Since you changed the method signature of initialize (caught because the arity changed from 2 to 1), it failed when it tried to create a new Config instance internally (why the exception happened on config.rb:5 where you override initialize)

You probably want to use composition and not a subclass that changes the method signature of initialize. Either create a function that accepts a filename and returns an ROS, or create another type called Config that has an accessor method that returns the ROS data structure.

class Config
  def initialize(filename)
    @filename = filename
    @config = RecursiveOpenStruct.new(YAML.load_file(@filename), {recurse_over_arrays: true, preserve_original_keys: true})
  end

  attr_accessor :config
end

my_config = Config.new("config.yml")

my_config.config
# => returns the ROS

my_config.config.devices
# => should return the devices sub-ROS

If you don't want the extra accessor in there, you could use a delegator pattern so that your Config forwards messages it doesn't implement on to the ROS structure.