Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: easy way to allow customizing classes #133

Closed
fabien-michel opened this issue Apr 4, 2024 · 7 comments
Closed

feat: easy way to allow customizing classes #133

fabien-michel opened this issue Apr 4, 2024 · 7 comments

Comments

@fabien-michel
Copy link
Collaborator

fabien-michel commented Apr 4, 2024

Add a mechanism to allow overriding classes.

Permit it would allow users to implement custom behavior without having to monkey-patch the code.

Example:

class MyCustomRepetition(Repetition):
    def __init__(self, *args, **kwargs):
        # custom behavior
        pass

class MyCustomRepeatedEvent(Repetition):
    def __init__(self, *args, **kwargs):
        # custom behavior
        pass

# Pass classes to `of` method (which will propagate options)
recurring_ical_events.of(calendar, repetition_cls=MyCustomRepetition, repeated_event_cls=MyCustomRepeatedEvent)

# or maybe with a dict
class_overrides = {
  Repetition: MyCustomRepetition,
  RepeatedEvent: MyCustomRepeatedEvent,
}

recurring_ical_events.of(calendar, class_overrides=class_overrides)

I'm not sure what is the good to way to do this kind of library customization.


We're using Polar.sh so you can upvote and help fund this issue. We receive the funding once the issue is completed & confirmed by you. Thank you in advance for helping prioritize & fund our work. Fund with Polar
@niccokunzmann
Copy link
Owner

niccokunzmann commented Apr 4, 2024

recurring_ical_events.of(calendar, class_overrides=class_overrides)

I like that one as it is simpler.

We have a start already. Maybe that can be incorporated. How to do it is now known to me at the moment.

recurrence_calculators = {

On a general note, I see that the amount of arguments being dragged through the code increases:

def of(a_calendar, keep_recurrence_attributes=False, components=["VEVENT"]):

This might be a point where a Strategy Pattern is a nicer solution. I think, people are fine with the odd argument and are unlikely to patch everything.

A Strategy pattern might also allow customization in the case of #132.

@fabien-michel
Copy link
Collaborator Author

I'm not sure what you mean by using the Strategy Pattern.
Would something like this will match your expectation ?

Customization

import recurring_ical_events

class CustomRepeatedEvent(recurring_ical_events.RepeatedEvent):
    def _get_component_end(self):
        # Customized method
        return super()._get_component_end()

class CustomUnfoldableCalendar(recurring_ical_events.UnfoldableCalendar):
    keep_recurrence_attributes = True
    recurrence_calculators = {
        **recurring_ical_events.UnfoldableCalendar.recurrence_calculators,
        "VEVENT": CustomRepeatedEvent,
    }

calendar_events = recurring_ical_events.of(calendar, unfoldable_calendar_cls=CustomUnfoldableCalendar).between(...)

Possible implementation

def of(
    a_calendar,
    keep_recurrence_attributes=False,
    components=None,
    unfoldable_calendar_cls = UnfoldableCalendar,
) -> UnfoldableCalendar,:
    ...
    return unfoldable_calendar_cls(a_calendar, keep_recurrence_attributes, components)
class UnfoldableCalendar:
    recurrence_calculators = {
        "VEVENT": RepeatedEvent,
        "VTODO": RepeatedTodo,
        "VJOURNAL": RepeatedJournal,
    }

    keep_recurrence_attributes = False  # Add this
    components = None   # Add this

    def __init__(
        self,
        calendar,
        keep_recurrence_attributes=None,
        components=None,
    ):
        # Keep existing behavior
        if keep_recurrence_attributes is not None:
            self.keep_recurrence_attributes = keep_recurrence_attributes
        self.components = components or ["VEVENT"]
        ...

@niccokunzmann
Copy link
Owner

niccokunzmann commented Jun 22, 2024

@fabien-michel, sorry for taking so long. I must have missed your comment. It looks good to me what you propose.
There are also other classes that are used further inside. It would be nice to have a consistent way to patch those.

If you like, you could create an example file that overrides all the classes so that people have it easier and consistent and I can make sure that it works in the future.

@niccokunzmann
Copy link
Owner

niccokunzmann commented Aug 20, 2024

In Version 3.0.0, I added a new way: the ComponentAdapter.

You can pass the ComponentAdapter to the components=[...] argument and it will be used to collect icalendar Components, create a Series and calculate the Occurrences. Thus, you can change all the components and classes if you like.

I would say that it is necessary to document the public interface a bit better for this: Which classes/methods/functions are public how to override the classes, more documentation in the methods.

Does this answer your question? What would you like to see to customize? Would documentation help? Let me know what you think, please.

@fabien-michel
Copy link
Collaborator Author

Thanks for all the works on version 3 !
They way you did it seems nice !
I'm just wondering how you would override Series class if needed. It's seems possible to override collect_components to return a new Series-like class, but I'm not sure if there is better way.

@niccokunzmann
Copy link
Owner

I should add an example that filters, and shows how to do it...

This was referenced Sep 9, 2024
@niccokunzmann
Copy link
Owner

Please check with v3.3.0 if this can be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants