1
votes

Méthode soignée pour obtenir et définir les clés de la colonne json dans Rails

J'ai un modèle / table avec une colonne json comme suit

def key1
  options['details']&.[]('key1')
end

def key1=(value)
  options['details'] ||= {}
  options['details']['key1'] ||=0 
  options['details']['key1'] += value 
end

La colonne peut contenir de nombreuses clés, quelque chose comme ça

options = {"details" : {key1: "Value1", key2: "Value2"}}


3 commentaires

options ['details'] &. [] ('key1') Manière intelligente d'utiliser l'opérateur sûr!


@Tom serait une façon intelligente d'utiliser Hash # default_proc :)


@AlekseiMatiushkin Oui, ce serait un moyen. L'utilisation de la forme longue de Hash # [] pour que l'opérateur sûr puisse être utilisé montre une bonne compréhension de Ruby. Je voulais le souligner comme un petit truc sympa auquel on ne penserait généralement pas.


3 Réponses :


3
votes

Utiliser la création de méthode dynamique:

options['details'].default_proc = ->(_,_) {{}}
ALLOWED_KEYS = %i[key1 key2 key3]

ALLOWED_KEYS.each do |key|
  define_method key do
    options['details'][key] if options['details'].key?(key)
  end
  define_method "#{key}=" do |value|
    (options['details'][key] ||= 0) += value
  end
end


3 commentaires

define_method "# {key} =", la valeur do doit être define_method "# {key} =" do | value | sinon une bonne réponse. Sinon, -> (_, _) {{}} pourrait être remplacé par proc {{}} pour rendre la réponse moins cryptique, mais ce n'est qu'une question de goût personnel .


Je viens également de réaliser que vous ne pouvez pas accéder aux options dans le contexte de la classe, uniquement dans le contexte de l'instance. Cela signifie que cette ligne entraînerait une erreur: options ['details']. Default_proc = -> (_, _) {{}}


@JohanWentholt Merci pour do | value | , bien sûr, mis à jour. Pour le reste: je ne me suis jamais engagé à écrire le code gratuitement. Je viens de montrer la solution de travail. Où mettre l'initialisation (initialiseur?) Appartient à OP.



0
votes

Vous pouvez également transmettre la clé en tant que paramètre, n'est-ce pas?

  def get_key key=:key1
   options['details']&.[](key)
  end

  def set_key= value, key=:key1
   options['details'] ||= {}
   options['details'][key] ||=0 
   options['details'][key] += value 
  end


1 commentaires

Comment cela répond-il à une question sur la création de getters et de setters?



0
votes

Simple et court

En fonction de la réutilisation, vous pouvez choisir différentes options. L'option courte est de définir simplement les méthodes en utilisant une boucle en combinaison avec #define_method .

class SomeModel < ApplicationRecord
  extend OptionDetailsAttributeAccessors

  option_details_attr_accessor :key1, :key2
end

Ecrire un module

Sinon, vous pouvez écrire un module qui fournit ci-dessus comme aides. Un module simple pourrait ressembler à ceci:

# app/model_helpers/option_details_attribute_accessors.rb
module OptionDetailsAttributeAccessors
  def option_details_attr_reader(*accessors)
    accessors.map(&:to_s).each do |accessor|
      define_method accessor do
        options.dig('details', accessor)
      end
    end
  end

  def option_details_attr_writer(*accessors)
    accessors.map(&:to_s).each do |accessor|
      define_method "#{accessor}=" do |value|
        details = options['details'] ||= {}
        details[accessor] ||= 0
        details[accessor] += value
      end
    end
  end

  def option_details_attr_accessor(*accessors)
    option_details_attr_reader(*accessors)
    option_details_attr_writer(*accessors)
  end
end

Maintenant, vous pouvez simplement étendre votre classe avec ces aides et ajouter facilement des lecteurs / rédacteurs.

class SomeModel < ApplicationRecord
  option_accessors = ['key1', 'key2']

  option_accessors.map(&:to_s).each do |accessor_name|
    #                     ^ in case you provide symbols in option_accessors
    #                       this can be left out if know this is not the case

    define_method accessor_name do
      options.dig('details', accessor_name)
    end

    define_method "#{accessor_name}=" do |value|
      details = options['details'] ||= {}
      details[accessor_name] ||= 0
      details[accessor_name] += value
    end
  end
end

0 commentaires