Metadata-Version: 2.1
Name: strify
Version: 1.0.0
Summary: The library provides a lightweight API using which you can in a minute add a pattern processing function "stringify(pattern: str) -> str" to any class.
Home-page: https://gitlab.com/aduard.kononov/strify
Author: Eduard Konanau
Author-email: aduard.kononov@gmail.com
License: UNKNOWN
Description: # Overview
        
        The library allows you to get a string representation of a class at **RUNTIME**, without overloading ```__repr__```, ```__str__```
        or defining your own specific ```get_repr() -> str``` (or even several for different cases).  
        The library provides a *lightweight* API using which you can _in a minute_ add a pattern processing function
        ```stringify(pattern: str) -> str``` to any class.  
        
        This tool is more **intended to help developers** that want to **allow users specify their own
        data representation patterns**.
        
        # Motivation  
        You're a developer. Let's assume that ```song_downloader``` is a library you wrote using ```strify```. ```song_downloader.search_in_web()``` searchs for a song 
        on the Internet and returns an instance of ```Song``` class that allows to download the song using
        ```Song.download_mp3()```  
        
        ```python
        >>> from song_downloader import search_in_web
        >>> song = search_in_web(title='Loosing My Mind', artist='Falling In Reverse')
        >>> print(song)
        Song(Loosing My Mind, Falling In Reverse, 2018)
        >>> song.download_mp3('[artist] — [title] ([year])')
        ```
        
        The code will download the mp3 and save it as "Falling In Reverse — Loosing My Mind (2018).mp3".
        
        You may ask: "**why should I use ```strify```** if I could do that like \*the code below\*?"
        ```python
        >>> from song_downloader import search_in_web
        >>> song = search_in_web(title='Loosing My Mind', artist='Falling In Reverse')
        >>> print(song)
        Song(Loosing My Mind, Falling In Reverse, 2018)
        >>> song.download_mp3(f'{song.artist} — {song.title} ({song.year})')
        ```
        
        You definitely can. But there is *no flexibility* for the end user that can't change sources;
        usually, he/she just is not assumed to do that, it's not a good practice.
        
        ```strify```'s approach is to **ask a user to enter whatever pattern he/she wants** and supply it to
        the program in any way (args, json, data base etc.) 
        and just use the value in the script. That's the power: **it's not necessary to define a pattern in the source code**.
        
        Now we can change the example and save it as ```download_mp3.py```:
        ```python
        from song_downloader import search_in_web
        args = parse_args()
        song = search_in_web(title=args['title'], artist=args['artist'])
        song.download_mp3(args['mp3_name_pattern'])
        ```
        
        Then run it like this:
        ```
        python3 download_mp3.py --title='loosing my mind' --artist='falling in reverse' --mp3-name-pattern='[artist] — [title] ([year])'
        ```
        And check what's happened:
        ```shell script
        user@computer:~/$ ls -1
        ...
        download_mp3.py
        Falling In Reverse — Loosing My Mind (2018).mp3
        ...
        user@computer:~/$
        ```
        
        # Usage Guide
        Let's continue with our Song class:
        ```python
        from random import randrange
        
        class Song:
            def __init__(self, title, artist):
                self.title = title
                self.artist = artist
            
            def get_year(self):
                year = randrange(1960, 2020)
                return year
            
            def download(self, file_name):
                with open(file_name, 'wb') as mp3:
                    mp3.write(b'010101010101010')
        ```
        **Notice**: ```Song.get_year()``` and ```Song.download()``` are mocks. In a real code they have to access the net and
        find all the data there using ```self.title``` and ```self.artist```.
        
        ### Diving in
        First of all, you need to take a look at the terminology:
        * **stringification** — process of building class instance representation (according to a **pattern**)   
        * **pattern** — a string with **markers**. After **stringification** all the markers 
        will be replaced with **marker values** (or ```final_marker_value```, see **preprocessing function**)
        * **marker** — ```f'[{marker_name}]'```
        * **marker attribute** — name of a class instance attribute which value is used during **stringification**.  
        * **marker value**: the way ```strify``` gets **marker value** looks like this:
        ```python
        import inspect
        def get_marker_value(class_instance, marker_attribute):
            marker_value = getattr(class_instance, marker_attribute)
            if inspect.ismethod(marker_value):
                marker_value = marker_value()
            return str(marker_value)
        ```
        * **preprocessing function**:
        ```python
        def preprocessing_function(marker_value: str) -> str:
            final_marker_value = whatever_magic_you_want(marker_value)
            return final_marker_value
        ```
        
        
        #### Way #1: use ```stringifyable``` and ```StringifyInfo```
        ```python
        from strify import stringifyable, StringifyInfo
        
        from random import randrange
        
        # transforms 'tHis striNg' into 'This String'
        def format_proper_name(string):
            return ' '.join(word[0].upper() + word[1:].lower() for word in string.split(' '))
        
        @stringifyable([
            StringifyInfo('title', 'title', format_proper_name),
            StringifyInfo('artist', 'artist', format_proper_name),
            StringifyInfo('year', 'get_year'),
        ])
        class Song:
            def __init__(self, title, artist):
                self.title = title
                self.artist = artist
            
            def get_year(self):
                year = randrange(1960, 2020)
                return year
            
            def download(self, file_name):
                with open(file_name, 'wb') as mp3:
                    mp3.write(b'010101010101010')
        ```
        
        ```stringifyable``` gets list of ```StringifyInfo``` and adds ```stringify(pattern: str) -> str```
        method to the class it decorates.  
        One ```StringifyInfo``` in the list describes one **marker**.  
        Signature:  
        ```StringifyInfo(marker_name, marker_attribute, preprocessing_function=None)```
        
        Now, the following is possible:
        ```python
        >>> song = Song('loosing my mind', 'falling in reverse')
        >>> song.stringify('[artist] — [title] ([year])')
        Falling In Reverse — Loosing My Mind (2018)
        >>> song.stringify('[title]: [artist], [year]')
        Loosing My Mind: Falling In Reverse, 2018
        ```
        
        #### Way #2: use ```stringifyable``` and ```stringify```
        ```python
        from strify import stringifyable, stringify
        
        from random import randrange
        
        # transforms 'tHis striNg' into 'This String'
        def format_proper_name(string):
            return ' '.join(word[0].upper() + word[1:].lower() for word in string.split(' '))
        
        @stringifyable()
        class Song:
            def __init__(self, title, artist):
                self._title = title
                self._artist = artist
            
            @stringify(format_proper_name)
            def artist(self):
                return self._artist
        
            @stringify(format_proper_name)
            def title(self):
                return self._title
        
            @stringify()
            def get_year(self):
                year = randrange(1960, 2020)
                return year
            
            def download(self, file_name):
                with open(file_name, 'wb') as mp3:
                    mp3.write(b'010101010101010')
        ```
        **Notice**: it would be more usual to make properties.
        In this case, ```stringify``` **must** be the first decorator that's applied to the function.
        ```python
        @property
        @stringify(format_proper_name)
        def title(self):
            return self._title
        ```
        
        Now you can test the code we ran at the end of **Way #1** and ensure that we achieved the same interface and results:
        ```python
        >>> song = Song('loosing my mind', 'falling in reverse')
        >>> song.stringify('[artist] — [title] ([year])')
        Falling In Reverse — Loosing My Mind (2018)
        >>> song.stringify('[title]: [artist], [year]')
        Loosing My Mind: Falling In Reverse, 2018
        ```
Platform: UNKNOWN
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.6
Description-Content-Type: text/markdown
