Auto capitalize enums name in Ruby (#10454) (#10763)

This closes #1965.
diff --git a/ruby/compatibility_tests/v3.0.0/tests/basic.rb b/ruby/compatibility_tests/v3.0.0/tests/basic.rb
index 7228144..d45c196 100755
--- a/ruby/compatibility_tests/v3.0.0/tests/basic.rb
+++ b/ruby/compatibility_tests/v3.0.0/tests/basic.rb
@@ -667,8 +667,8 @@
       assert m["z"] == :C
       m["z"] = 2
       assert m["z"] == :B
-      m["z"] = 4
-      assert m["z"] == 4
+      m["z"] = 5
+      assert m["z"] == 5
       assert_raise RangeError do
         m["z"] = :Z
       end
diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c
index 6b8bbaa..31d7dbb 100644
--- a/ruby/ext/google/protobuf_c/message.c
+++ b/ruby/ext/google/protobuf_c/message.c
@@ -1290,15 +1290,20 @@
   int n = upb_EnumDef_ValueCount(e);
   for (int i = 0; i < n; i++) {
     const upb_EnumValueDef* ev = upb_EnumDef_Value(e, i);
-    const char* name = upb_EnumValueDef_Name(ev);
+    char* name = strdup(upb_EnumValueDef_Name(ev));
     int32_t value = upb_EnumValueDef_Number(ev);
     if (name[0] < 'A' || name[0] > 'Z') {
-      rb_warn(
+      if (name[0] >= 'a' && name[0] <= 'z') {
+        name[0] -= 32; // auto capitalize
+      } else {
+        rb_warn(
           "Enum value '%s' does not start with an uppercase letter "
           "as is required for Ruby constants.",
           name);
+      }
     }
     rb_define_const(mod, name, INT2NUM(value));
+    free(name);
   }
 
   rb_define_singleton_method(mod, "lookup", enum_lookup, 1);
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java
index 6532867..0eb7c93 100644
--- a/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java
@@ -162,9 +162,10 @@
     boolean defaultValueRequiredButNotFound =
         descriptor.getFile().getSyntax() == FileDescriptor.Syntax.PROTO3;
     for (EnumValueDescriptor value : descriptor.getValues()) {
-      String name = value.getName();
-      // Make sure its a valid constant name before trying to create it
-      if (Character.isUpperCase(name.codePointAt(0))) {
+      String name = fixEnumConstantName(value.getName());
+      // Make sure it's a valid constant name before trying to create it
+      int ch = name.codePointAt(0);
+      if (Character.isUpperCase(ch)) {
         enumModule.defineConstant(name, runtime.newFixnum(value.getNumber()));
       } else {
         runtime
@@ -189,6 +190,22 @@
     return enumModule;
   }
 
+  private static String fixEnumConstantName(String name) {
+    if (name != null && name.length() > 0) {
+      int ch = name.codePointAt(0);
+      if (ch >= 'a' && ch <= 'z') {
+        // Protobuf enums can start with lowercase letters, while Ruby's constant should
+        // always start with uppercase letters. We tolerate this case by capitalizing
+        // the first character if possible.
+        return new StringBuilder()
+                .appendCodePoint(Character.toUpperCase(ch))
+                .append(name.substring(1))
+                .toString();
+      }
+    }
+    return name;
+  }
+
   private EnumDescriptor descriptor;
   private EnumDescriptorProto.Builder builder;
   private IRubyObject name;
diff --git a/ruby/tests/basic_test.proto b/ruby/tests/basic_test.proto
index fb70f47..d480d48 100644
--- a/ruby/tests/basic_test.proto
+++ b/ruby/tests/basic_test.proto
@@ -73,6 +73,7 @@
   A = 1;
   B = 2;
   C = 3;
+  v0 = 4;
 }
 
 message TestEmbeddedMessageParent {
diff --git a/ruby/tests/basic_test_proto2.proto b/ruby/tests/basic_test_proto2.proto
index 0c1a2b9..ac705ed 100644
--- a/ruby/tests/basic_test_proto2.proto
+++ b/ruby/tests/basic_test_proto2.proto
@@ -69,6 +69,7 @@
   A = 1;
   B = 2;
   C = 3;
+  v0 = 4;
 }
 
 enum TestNonZeroEnum {
diff --git a/ruby/tests/common_tests.rb b/ruby/tests/common_tests.rb
index 5918c8a..9288425 100644
--- a/ruby/tests/common_tests.rb
+++ b/ruby/tests/common_tests.rb
@@ -331,14 +331,16 @@
     l.push :A
     l.push :B
     l.push :C
-    assert l.count == 3
+    l.push :v0
+    assert l.count == 4
     assert_raise RangeError do
       l.push :D
     end
     assert l[0] == :A
+    assert l[3] == :v0
 
-    l.push 4
-    assert l[3] == 4
+    l.push 5
+    assert l[4] == 5
   end
 
   def test_rptfield_initialize
@@ -542,8 +544,8 @@
     assert m["z"] == :C
     m["z"] = 2
     assert m["z"] == :B
-    m["z"] = 4
-    assert m["z"] == 4
+    m["z"] = 5
+    assert m["z"] == 5
     assert_raise RangeError do
       m["z"] = :Z
     end
@@ -712,14 +714,17 @@
     assert proto_module::TestEnum::A == 1
     assert proto_module::TestEnum::B == 2
     assert proto_module::TestEnum::C == 3
+    assert proto_module::TestEnum::V0 == 4
 
     assert proto_module::TestEnum::lookup(1) == :A
     assert proto_module::TestEnum::lookup(2) == :B
     assert proto_module::TestEnum::lookup(3) == :C
+    assert proto_module::TestEnum::lookup(4) == :v0
 
     assert proto_module::TestEnum::resolve(:A) == 1
     assert proto_module::TestEnum::resolve(:B) == 2
     assert proto_module::TestEnum::resolve(:C) == 3
+    assert proto_module::TestEnum::resolve(:v0) == 4
   end
 
   def test_enum_const_get_helpers
@@ -788,7 +793,7 @@
     assert_raise(NoMethodError) { m.a }
     assert_raise(NoMethodError) { m.a_const_const }
   end
-  
+
   def test_repeated_push
     m = proto_module::TestMessage.new
 
@@ -1762,7 +1767,7 @@
     assert_raise(FrozenErrorType) { m.repeated_msg = proto_module::TestMessage2.new }
     assert_raise(FrozenErrorType) { m.repeated_enum = :A }
   end
-  
+
   def test_eq
     m1 = proto_module::TestMessage.new(:optional_string => 'foo', :repeated_string => ['bar1', 'bar2'])
     m2 = proto_module::TestMessage.new(:optional_string => 'foo', :repeated_string => ['bar1', 'bar2'])
diff --git a/ruby/tests/generated_code.proto b/ruby/tests/generated_code.proto
index bfdfa5a..5f017ba 100644
--- a/ruby/tests/generated_code.proto
+++ b/ruby/tests/generated_code.proto
@@ -67,6 +67,8 @@
   A = 1;
   B = 2;
   C = 3;
+
+  v0 = 4;
 }
 
 message testLowercaseNested {
diff --git a/ruby/tests/generated_code_proto2.proto b/ruby/tests/generated_code_proto2.proto
index 1e95721..1a50b84 100644
--- a/ruby/tests/generated_code_proto2.proto
+++ b/ruby/tests/generated_code_proto2.proto
@@ -68,6 +68,8 @@
   A = 1;
   B = 2;
   C = 3;
+
+  v0 = 4;
 }
 
 message TestUnknown {
diff --git a/ruby/tests/repeated_field_test.rb b/ruby/tests/repeated_field_test.rb
index 881810c..de96869 100755
--- a/ruby/tests/repeated_field_test.rb
+++ b/ruby/tests/repeated_field_test.rb
@@ -697,6 +697,7 @@
       value :A, 1
       value :B, 2
       value :C, 3
+      value :v0, 4
     end
   end