-3

I got this following function in Ruby which uses gsub.

def class_to_endpoint(klass)
  klass.name.split('::').last.
    gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
    gsub(/([a-z\d])([A-Z])/,'\1_\2').
    tr("-", "_").
    downcase
end 

How can I implement this in Python re? Please help me

Tried this on irb console and and I can give few examples, basically adds an underscore bw each word in camelcase syntax

  1. UserProfile -> user_profile
  2. LastModifiedTime -> last_modified_time
  3. User-Profile -> user_profile

Answer: I think this is all I wanted -> Elegant Python function to convert CamelCase to snake_case?

Copied from the above link and modified a bit

def class_to_endpoint(name):
    s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
    return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).replace('-', '_').lower()
Community
  • 1
  • 1
user4k
  • 191
  • 1
  • 2
  • 11

2 Answers2

2

The idea is to get a class and convert its CamelCase name to a snake_case one.

Let me try to explain it:

klass.name.split('::').last

Namespaces in ruby are achieved through nesting classes in modules or other classes. For example:

module API
  class Service2URLMapper
  end
end

Now Service2URLMapper can be referenced with API::Service2URLMapper. This is what is given to the method here. Splitting by :: and getting the last element will give you the name of the class without the namespace prefix. In this case Service2URLMapper.

gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')

This will split any sequence of two or more capital letters at the position of the last capital letter with a _ if the sequence is followed by a lowercase letter. For example:

"Service2URLMapper".gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2') # => "Service2URL_Mapper"

The next part is

gsub(/([a-z\d])([A-Z])/,'\1_\2')

which will similarly split after a digit, if it was preceded by a lower case letter and followed by a capital letter:

"Service2URL_Mapper".gsub(/([a-z\d])([A-Z])/,'\1_\2') # => "Service2_URL_Mapper"

The two regexes should be the same in python.

After that tr("-", "_") will simply replace - with _. I have no idea who uses dashes in their class names, but apparently the author decided it was necessary.

And finally downcase just does what you would expect.

So everything in action gives us:

class_to_endpoint(API::Service2URLMapper) # => "service2_url_mapper"
ndnenkov
  • 35,425
  • 9
  • 72
  • 104
  • The code seems to be from ActiveSupport's underscore method from strings, which is even more complex nowadays: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L91-L100, see also this question: http://stackoverflow.com/questions/1509915/converting-camel-case-to-underscore-case-in-ruby – Bruno E. Aug 08 '15 at 07:43
  • @BrunoE., that is what I thought. Still, what is this `-` to `_` spiel? Can you give an example of a famous class with a dash in its name? – ndnenkov Aug 08 '15 at 07:49
  • @ndn thanks a lot for the explanation. seems like this one does it. http://stackoverflow.com/questions/1175208/elegant-python-function-to-convert-camelcase-to-camel-case – user4k Aug 08 '15 at 07:49
  • @ndn found https://github.com/rails/rails/commit/6c95e9b14697a9527ab761d1a084f6bc61af56b9 which introduced it long time ago – Bruno E. Aug 08 '15 at 07:58
2

A python one-liner will do this job.

>>> import re
>>> s = ['LastModifiedTime','UserProfile','User-Profile']
>>> [re.sub(r'(^|([a-z])\W?)([A-Z])', lambda m: m.group(2)+'_'+ m.group(3).lower() if m.group(1) else m.group(3).lower(), i) for i in s]
['last_modified_time', 'user_profile', 'user_profile']
Avinash Raj
  • 172,303
  • 28
  • 230
  • 274
  • Marking this answer as a correct answer as it has the Python code. Which matches the title of the question. For every other reader's good. – user4k Aug 08 '15 at 16:03