class FieldMaskedModel::Base

Attributes

inaccessible_error_callback[R]
model_pool[R]
msg[R]

Public Class Methods

dispatch(entry) click to toggle source

@param [Google::Protobuf::FieldDescriptor] entry @return [Symbol]

# File lib/field_masked_model/base.rb, line 76
def dispatch(entry)
  case entry.type
  when :message
    m = @model_pool.lookup(entry.subtype.msgclass)
    # NOTE: If msgclass is not registered in ModelPool, we treat it
    # as a one of attribute classes.
    if m.nil?
      return :attribute
    end

    case entry.label
    when :repeated
      :repeated_association
    else
      :association
    end
  else # eum or scalar
    :attribute
  end
end
entries() click to toggle source

@return [<Google::Protobuf::FieldDescriptor>]

# File lib/field_masked_model/base.rb, line 68
def entries
  @msg.descriptor.entries.select do |e|
    !_excluded_fields.include?(e.name)
  end
end
exclude_field(name) click to toggle source

@param [Symbol, String] name

# File lib/field_masked_model/base.rb, line 33
def exclude_field(name)
  if defined?(@msg)
    Kernel.warn("exclude_field must be called before msgclass", uplevel: 1)
  end
  _excluded_fields << name.to_s
end
fields() click to toggle source

@return [<Symbol, { Symbol => Array }>]

# File lib/field_masked_model/base.rb, line 46
def fields
  r = []
  children = {}
  self.entries.each do |entry|
    n = entry.name.to_sym
    type = self.dispatch(entry)
    case type
    when :attribute
      r << n
    when :association
      modelclass = @model_pool.lookup(entry.subtype.msgclass)
      children[n] = modelclass.fields
    when :repeated_association
      modelclass = @model_pool.lookup(entry.subtype.msgclass)
      children[n] = modelclass.fields
    end
  end
  r << children if children.size > 0
  r
end
msgclass(klass, exclude_fields: [], model_pool: ModelPool.generated_pool) click to toggle source

@param [Class] klass A class represents the protobuf message class @param [<Symbol, String>] exclude_fields @param [FieldMaskedModel::ModelPool] model_pool

# File lib/field_masked_model/base.rb, line 16
def msgclass(klass, exclude_fields: [], model_pool: ModelPool.generated_pool)
  if defined?(@msg)
    raise "msgclass is already registered!"
  end

  @model_pool = model_pool
  @model_pool.add(klass, self)

  exclude_fields.each do |field|
    exclude_field field
  end

  @msg = klass
  define_accessors!
end
new(field_mask: nil, field_mask_node: nil, message:) click to toggle source

@param [Google::Protobuf::FieldMask, nil] field_mask_node @param [FMParser::MessageNode, nil] field_mask_node @param [Object] message represents the protobuf message object

# File lib/field_masked_model/base.rb, line 151
def initialize(field_mask: nil, field_mask_node: nil, message:)
  if field_mask.nil? && field_mask_node.nil?
    raise ArgumentError.new("missing keyword: field_mask or field_mask_node")
  end
  @fm_node = field_mask_node || FMParser.parse(paths: field_mask.paths, root: self.class.msg)
  @message = message

  @accessible_fields ||= Set.new(@fm_node.field_names)
end
set_inaccessible_error_callback(callback) click to toggle source

@param [Proc] callback

# File lib/field_masked_model/base.rb, line 41
def set_inaccessible_error_callback(callback)
  @inaccessible_error_callback = callback
end

Private Class Methods

_excluded_fields() click to toggle source
# File lib/field_masked_model/base.rb, line 143
def _excluded_fields
  @_excluded_fields ||= Set.new
end
define_accessor!(name:, entry:) click to toggle source
# File lib/field_masked_model/base.rb, line 106
def define_accessor!(name:, entry:)
  define_method(name) do
    validate!(name)

    ivar = :"@_#{name}"
    if instance_variable_defined?(ivar)
      next instance_variable_get(ivar)
    end

    v = @message.send(name)
    type = self.class.dispatch(entry)
    r =
      case type
      when :attribute
        AttributeConverter.convert(v)
      when :association
        if v.nil?
          nil
        else
          modelclass = self.class.model_pool.lookup(entry.subtype.msgclass)
          modelclass.new(field_mask_node: @fm_node.get_child(name), message: v)
        end
      when :repeated_association
        modelclass = self.class.model_pool.lookup(entry.subtype.msgclass)
        v.map do |vv|
          if vv.nil?
            nil
          else
            modelclass.new(field_mask_node: @fm_node.get_child(name), message: vv)
          end
        end
      end

    instance_variable_set(ivar, r)
  end
end
define_accessors!() click to toggle source
# File lib/field_masked_model/base.rb, line 99
def define_accessors!
  self.entries.each do |entry|
    n = entry.name.to_sym
    define_accessor!(name: n, entry: entry)
  end
end

Public Instance Methods

inspect() click to toggle source

@return [String]

# File lib/field_masked_model/base.rb, line 187
def inspect
  h = {}
  self.class.entries.each do |entry|
    n = entry.name.to_sym
    if !@accessible_fields.include?(n)
      h[n] = "-"
      next
    end

    v = self.send(n)
    type = self.class.dispatch(entry)
    case type
    when :attribute
      case v
      when NilClass
        h[n] = "nil"
      when String
        h[n] = "\"#{v}\""
      else
        h[n] = v
      end
    when :association
      case v
      when NilClass
        h[n] = "nil"
      else
        h[n] = v.class.name.split("::").last
      end
    when :repeated_association
      if v.size > 0
        h[n] = "[#{v[0].class.name.split("::").last}]"
      else
        h[n] = "[]"
      end
    end
  end
  "<#{self.class.name}#{h.map { |k, v| "\n #{k}: #{v}" }.join(',')}>"
end
to_h() click to toggle source

@return [Hash]

# File lib/field_masked_model/base.rb, line 162
def to_h
  r = {}
  self.class.entries.each do |entry|
    n = entry.name.to_sym
    next if !@accessible_fields.include?(n)

    v = self.send(n)
    type = self.class.dispatch(entry)
    case type
    when :attribute
      r[n] = v
    when :association
      if v.nil?
        r[n] = nil
      else
        r[n] = v.to_h
      end
    when :repeated_association
      r[n] = v.map(&:to_h)
    end
  end
  r
end

Private Instance Methods

validate!(field) click to toggle source

@param [Symbol] field @raise [FieldMaskedModel::InaccessibleError]

# File lib/field_masked_model/base.rb, line 230
def validate!(field)
  if !@accessible_fields.include?(field)
    if self.class.inaccessible_error_callback
      self.class.inaccessible_error_callback.call(field)
    else
      raise FieldMaskedModel::InaccessibleError.new("`#{field}` is not specified as paths in field_mask!")
    end
  end
end