Suppose we have a certain configuration structure (taken from YAML , JSON , XML or simply as a Hash ):

 configuration = { gmail: { username: 'example@gmail.com', password: 'pa$$word', host: 'imap.gmail.com', ssl: true, port: 993 }, ftp: { username: 'example@gmail.com', password: 'pa$$word', host: 'imap.gmail.com', ssl: true, port: 42 } } 

Further, on the basis of this structure, we obtain data from it:

 mail = Mail.new host: configuration[:gmail][:host], port: configuration[:gmail][:port], username: configuration[:gmail][:username], password: configuration[:gmail][:password], ssl: configuration[:gmail][:ssl] ftp = FTP.new host: configuration[:ftp][:host], port: configuration[:ftp][:port], username: configuration[:ftp][:username], password: configuration[:ftp][:password], ssl: configuration[:ftp][:ssl] 

Everything works, but the code itself is “poorly readable”. That is, it is of course understandable, but too much “dry” text, instead of the usual code for the programming language.

Share Best Practice , how to do and use configurations in Ruby .

  • 2
    This answer can help you. I can not immediately say, duplicate your question or not. - D-side

2 answers 2

If the names of the Mail and FTP attributes in your example correspond exactly to the keys in the structures in the configuration, you can send the hash directly to the constructor:

 mail = Mail.new configuration[:gmail] ftp = FTP.new configuration[:ftp] 

Here it is important to remember two things:

  • The keys and value types in the configuration must match the attributes and value types of the class attributes;
  • Only you should have access to the configuration - otherwise the attacker, using the structure, can create objects with any class attributes without restrictions.

    Well, at least you wrote a selection of a subset of hash with hard keys . "Column" in your code (which is obtained if the code is "trim"):

     mail = Mail.new host: configuration[:gmail][:host], port: configuration[:gmail][:port], username: configuration[:gmail][:username], password: configuration[:gmail][:password], ssl: configuration[:gmail][:ssl] # ^^^^^^^^^^^^^^^^^^ это ^^^^^^^^^^^^^^^^^^^^ 

    ... really just a hashmap. Just {} around it was optional to write, since this is the last argument of the method call, and this is a special case in the Ruby syntax.

    If there is no unnecessary keys in configuration[:gmail] , then you can simply do this:

     Mail.new configuration[:gmail] 

    ... and if you want to cut off the extra keys, and you are armed with aсtivesupport (this gem from Rails), there is a Hash#slice :

     # Точечная загрузка ActiveSupport require 'active_support/core_ext/hash/slice' # В Rails необязательно, там он обычно весь уже загружен Mail.new configuration[:gmail].slice(:host, :port, :username, :password, :ssl) 

    ... but usually one set of configuration parameters is used in exactly one place (or if in several, then at least the same: for example, when calling constructors of the same type), so you can not afford to just write extra keys in configs.

    This is not a convention, this situation has developed itself and it suits everyone.

    The "Best bractices" of the configurations that you are waiting for are mostly not concerned with this and they are packaged, in various combinations, in the dotenv , figaro and config gems. What is typically found in them:

    • The abbreviated syntax for obtaining a key configuration is: a[:b][:c] => abc
    • Fallback (when there is no config) to environment variables in ENV (see 12-factor annexes )
    • Convention on the placement of configuration files and their format - who is on that much