palkan / anyway_config

Configuration library for Ruby gems and applications
MIT License
778 stars 52 forks source link

to_hash method would be useful #4

Closed epistrephein closed 7 years ago

epistrephein commented 7 years ago

A to_hash or to_h method callable on MyCoolGem.config would be useful to pass the values around in bulk, without having to call the specific attribute name.
MyCoolGem.config.load kinda return an hash but it calls load_from_sources which may not be the behavior expected.

palkan commented 7 years ago

Hi!

Sorry for late response.

The idea make sense, but currently it's not so easy to implement, 'cause we do not store config parameters internally in a Hash (or smth similar) but in instance variables. So, maybe, it would be better to refactor the internal representation.

Another question: do you suppose that #to_hash method should return a new, read-only, Hash instance every time? And thus it cannot be used to modify the configuration. I think so.

epistrephein commented 7 years ago

My initial thought was being able to write a method which accepts double splat kwargs (**kwargs), pass to it the whole @config var and let the method use the config attributes it needs.

I would say that the hash returned should definitely be read-only, so that you don't accidentally edit the values thinking you're editing the configuration.

Currently the fastest way I found to achieve this without refactoring anything is with instance_variable_get like (with redundant self for clarity):

module MyCoolGem
  def self.config_hash
    self.config.instance_variables.map do |attribute|
      { attribute.to_s.tr('@', '').to_sym => self.config.instance_variable_get(attribute) }
    end
  end
end

It's intricate, needs to drop the @ in the symbol name and it's another method to write and call instead of something simpler like MyCoolGem.config.to_h. What do you think?

palkan commented 7 years ago

We can use config_attributes for that:

def to_h
  self.class.config_attributes.each_with_object({}) do |key, obj|
    obj[key.to_sym] = send(key)
  end.freeze
end

The above solution has one caveat: we can have complex values (Hash, Array), so we should deep_dup and freeze everything:

def to_h
  self.class.config_attributes.each_with_object({}) do |key, obj|
    ...
  end.deep_dup.deep_freeze
end

That should work.

epistrephein commented 7 years ago

Looks nice and maybe worth implementing. Though deep_dup is found in Rails, if i'm not wrong, so no plain Ruby solution :(

palkan commented 7 years ago

We have our own simplified deep_dup implemented through refinements; the same way we can add deep_freeze too.

epistrephein commented 7 years ago

Awesome! 👍

palkan commented 7 years ago

Released as v1.1.0