Module: Protobuf::Message::Fields::ClassMethods

Defined in:
lib/protobuf/message/fields.rb

Instance Method Summary collapse

Instance Method Details

#all_fieldsObject

Field Access Methods



81
82
83
# File 'lib/protobuf/message/fields.rb', line 81

def all_fields
  @all_fields ||= field_store.values.uniq.sort_by(&:tag)
end

#define_field(rule, type_class, fully_qualified_field_name, tag, options) ⇒ Object



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/protobuf/message/fields.rb', line 128

def define_field(rule, type_class, fully_qualified_field_name, tag, options)
  raise_if_tag_collision(tag, fully_qualified_field_name)
  raise_if_name_collision(fully_qualified_field_name)

  # Determine appropirate accessor for fields depending on name collisions via extensions:

  # Case 1: Base field = "string_field" and no extensions of the same name
  # Result:
  #   message.string_field #=> @values["string_field"]
  #   message[:string_field] #=> @values["string_field"]
  #   message['string_field'] #=> @values["string_field"]

  # Case 2: Base field = "string_field" and extension 1 = ".my_package.string_field", extension N = ".package_N.string_field"...
  # Result:
  #   message.string_field #=> @values["string_field"]
  #   message[:string_field] #=> @values["string_field"]
  #   message['string_field'] #=> @values["string_field"]
  #   message[:'.my_package.string_field'] #=> @values[".my_package.string_field"]
  #   message['.my_package.string_field']  #=> @values[".my_package.string_field"]

  # Case 3: No base field, extension 1 = ".my_package.string_field", extension 2 = ".other_package.string_field", extension N...
  # Result:
  #   message.string_field #=> raise NoMethodError (no simple accessor allowed)
  #   message[:string_field] #=> raise NoMethodError (no simple accessor allowed)
  #   message['string_field'] #=> raise NoMethodError (no simple accessor allowed)
  #   message[:'.my_package.string_field'] #=> @values[".my_package.string_field"]
  #   message['.my_package.string_field']  #=> @values[".my_package.string_field"]
  #   message[:'.other_package.string_field'] #=> @values[".other_package.string_field"]
  #   message['.other_package.string_field']  #=> @values[".other_package.string_field"]

  # Case 4: No base field, extension = ".my_package.string_field", no other extensions
  # Result:
  #   message.string_field #=> @values[".my_package.string_field"]
  #   message[:string_field] #=> @values[".my_package.string_field"]
  #   message['string_field'] #=> @values[".my_package.string_field"]
  #   message[:'.my_package.string_field'] #=> @values[".my_package.string_field"]
  #   message[:'.my_package.string_field'] #=> @values[".my_package.string_field"]

  simple_name =
    if options[:extension]
      base_name = fully_qualified_field_name.to_s.split('.').last.to_sym
      if field_store[base_name]
        # Case 3
        if field_store[base_name].extension?
          remove_existing_accessors(base_name)
        end
        nil
      # Case 4
      else
        base_name
      end
    else
      # Case 1
      fully_qualified_field_name
    end

  field = ::Protobuf::Field.build(self, rule, type_class, fully_qualified_field_name,
                                  tag, simple_name, options)
  field_store[tag] = field
  field_store[fully_qualified_field_name.to_sym] = field
  field_store[fully_qualified_field_name.to_s] = field
  if simple_name && simple_name != fully_qualified_field_name
    field_store[simple_name.to_sym] = field
    field_store[simple_name.to_s] = field
  end
  # defining a new field for the message will cause cached @all_fields, @extension_fields,
  # and @fields to be incorrect; reset them
  @all_fields = @extension_fields = @fields = nil
end

#extension_fieldsObject



85
86
87
# File 'lib/protobuf/message/fields.rb', line 85

def extension_fields
  @extension_fields ||= all_fields.select(&:extension?)
end

#extension_rangesObject



89
90
91
# File 'lib/protobuf/message/fields.rb', line 89

def extension_ranges
  @extension_ranges ||= []
end

#extension_tag?(tag) ⇒ Boolean

Returns:

  • (Boolean)


97
98
99
# File 'lib/protobuf/message/fields.rb', line 97

def extension_tag?(tag)
  tag.respond_to?(:to_i) && get_extension_field(tag).present?
end

#extensions(range) ⇒ Object

Define an extension range.



74
75
76
# File 'lib/protobuf/message/fields.rb', line 74

def extensions(range)
  extension_ranges << range
end

#field_storeObject



101
102
103
# File 'lib/protobuf/message/fields.rb', line 101

def field_store
  @field_store ||= {}
end

#field_tag?(tag, allow_extension = false) ⇒ Boolean

Returns:

  • (Boolean)


109
110
111
# File 'lib/protobuf/message/fields.rb', line 109

def field_tag?(tag, allow_extension = false)
  tag.respond_to?(:to_i) && get_field(tag, allow_extension).present?
end

#fieldsObject



105
106
107
# File 'lib/protobuf/message/fields.rb', line 105

def fields
  @fields ||= all_fields.reject(&:extension?)
end

#get_extension_field(name_or_tag) ⇒ Object



113
114
115
116
# File 'lib/protobuf/message/fields.rb', line 113

def get_extension_field(name_or_tag)
  field = field_store[name_or_tag]
  field if field.try(:extension?) { false }
end

#get_field(name_or_tag, allow_extension = false) ⇒ Object



118
119
120
121
122
123
124
125
126
# File 'lib/protobuf/message/fields.rb', line 118

def get_field(name_or_tag, allow_extension = false)
  field = field_store[name_or_tag]

  if field && (allow_extension || !field.extension?)
    field
  else
    nil
  end
end

#inherited(subclass) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/protobuf/message/fields.rb', line 21

def inherited(subclass)
  inherit_fields!(subclass)
  subclass.const_set("PROTOBUF_MESSAGE_REQUIRED_FIELD_TAGS", subclass.required_field_tags)
  subclass.const_set("PROTOBUF_MESSAGE_GET_FIELD", subclass.field_store)
  subclass.class_eval <<-RUBY, __FILE__, __LINE__
    def _protobuf_message_field
      PROTOBUF_MESSAGE_GET_FIELD
    end

    def _protobuf_message_unset_required_field_tags
      @_protobuf_message_unset_required_field_tags ||= PROTOBUF_MESSAGE_REQUIRED_FIELD_TAGS.dup
    end
  RUBY
end

#map(key_type_class, value_type_class, name, tag, options = {}) ⇒ Object

Define a map field.



61
62
63
64
65
66
67
68
69
70
# File 'lib/protobuf/message/fields.rb', line 61

def map(key_type_class, value_type_class, name, tag, options = {})
  # manufacture a message that represents the map entry, used for
  # serialization and deserialization
  entry_type = Class.new(::Protobuf::Message) do
    set_option :map_entry, true
    optional key_type_class, :key, 1
    optional value_type_class, :value, 2
  end
  define_field(:repeated, entry_type, name, tag, options)
end

#optional(type_class, name, tag, options = {}) ⇒ Object

Define an optional field.



42
43
44
# File 'lib/protobuf/message/fields.rb', line 42

def optional(type_class, name, tag, options = {})
  define_field(:optional, type_class, name, tag, options)
end

#raise_if_name_collision(field_name) ⇒ Object



217
218
219
220
221
# File 'lib/protobuf/message/fields.rb', line 217

def raise_if_name_collision(field_name)
  if get_field(field_name, true)
    fail DuplicateFieldNameError, %(Field name #{field_name} has already been used in "#{name}".)
  end
end

#raise_if_tag_collision(tag, field_name) ⇒ Object



211
212
213
214
215
# File 'lib/protobuf/message/fields.rb', line 211

def raise_if_tag_collision(tag, field_name)
  if get_field(tag, true)
    fail TagCollisionError, %(Field number #{tag} has already been used in "#{name}" by field "#{field_name}".)
  end
end

#remove_existing_accessors(accessor) ⇒ Object



198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/protobuf/message/fields.rb', line 198

def remove_existing_accessors(accessor)
  field_store.delete(accessor.to_sym).try(:fully_qualified_name_only!)
  field_store.delete(accessor.to_s)
  ACCESSOR_SUFFIXES.each do |modifier|
    begin
      remove_method("#{accessor}#{modifier}")
    # rubocop: disable Lint/HandleExceptions
    rescue NameError
      # Do not remove the method
    end
  end
end

#repeated(type_class, name, tag, options = {}) ⇒ Object

Define a repeated field.



48
49
50
# File 'lib/protobuf/message/fields.rb', line 48

def repeated(type_class, name, tag, options = {})
  define_field(:repeated, type_class, name, tag, options)
end

#required(type_class, name, tag, options = {}) ⇒ Object

Define a required field.



54
55
56
57
# File 'lib/protobuf/message/fields.rb', line 54

def required(type_class, name, tag, options = {})
  required_field_tags << tag
  define_field(:required, type_class, name, tag, options)
end

#required_field_tagsObject



93
94
95
# File 'lib/protobuf/message/fields.rb', line 93

def required_field_tags
  @required_field_tags ||= []
end