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

[WIP] Operation Refactor Sandbox #369

Closed
wants to merge 36 commits into from
Closed

Conversation

jwoertink
Copy link
Member

@jwoertink jwoertink commented May 21, 2020

Overrides #363

This will just be a saving sandbox for me to try out some refactors. The actual PRs will be broken out in to smaller ones with less commits allowing them to be easily digested.

The end goal will be:

  • Give a unified API between a SaveOperation and a non-db backed Operation
  • Allow Operation to use needs, and callbacks
  • Cleanup Operation API so the user doesn't need to define a ton of yield blocks
  • Moves SaveOperation to a similar API. This API should be very close to how it is now, but maybe with a few minor changes.
  • Removed all of the old interfaces that gave errors from deprecated previous versions
  • Cleaning up a few compiler errors around operations

Currently in WIP state, so these goals could change, or increase as I get further along in this.

Here's a list of issues that I would like to take a stab at fixing along with this. I can't guarantee all of them, but I want a single place to reference them as I go along.

…o be easier to find, and understand. A few specs are passing, but still need to port over SaveOperation to the new structure
@jwoertink jwoertink added clarify api Rename/remove/add something to make the API easier to understand BREAKING CHANGE This will cause a breaking change labels May 21, 2020
Copy link
Member

@matthewmcgarvey matthewmcgarvey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't gotten the chance to look through it all yet but I hope this is a useful thought

def after_save(_record : T) forall T
end

def after_commit(_record : T) forall T
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this module is included in Avram::Operation and I'm thinking that these callbacks don't make sense outside of database operations. Could this module be refactored to allow declaring life cycle events that be subscribed to? Then, Avram::Operation could declare before and after run events and Avram::SaveOperation could declare the others. Something like:

module Avram::Callbacks
  macro register_life_cycle_event(event_name, *args)
    macro {{ event_name.id }}(method_name)
      def {{ event_name.id }}(
        {% for param in args %}
          {{ param.id }},
        {% end %}
      )
        \{% if @type.methods.map(&.name).includes?(:before_run.id) %}
          previous_def
        \{% else %}
          super
        \{% end %}

        \{{ method_name.id }}
      end
    end
  end
end

And then in the operations:

abstract class Avram::Operation
  include Avram::Callbacks

  register_life_cycle_event :before_run
  register_life_cycle_event :after_run, object
end

abstract class Avram::SaveOperation(T) < Avram::Operation
  register_life_cycle_event :before_save
  register_life_cycle_event :after_save, record : T
  register_life_cycle_event :after_commit, record : T
end

I have no idea if this actually works but this allows Avram::Callbacks to be a generic utility and not add methods to Avram::Operation that shouldn't be there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a repo with a somewhat working example of my thoughts https://github.com/matthewmcgarvey/crystal-callbacks

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of subscribing to events. Right now there's quite a bit of duplication which I hope to cleanup before this PR is fully done. The one thing I'm unclear on is how we would differentiate between when a method would be called before certain things, or after. Like, this would not stop someone from writing register_life_cycle_event :around_save, but how would that work? I'll have to think on this more after I start to clean up some of the duplication. That might give me a little more clarity. Thanks for the suggestions!

Copy link
Member

@matthewmcgarvey matthewmcgarvey Sep 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jwoertink that repo linked above has been updated to work (kind of) as you are wanting. Base classes will call register_event :run and then there will be macro methods for before_run, after_run, and around_run. The macros got quite hairy 🙈

I referenced Rails' callback system for this https://api.rubyonrails.org/classes/ActiveSupport/Callbacks.html

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweet! I'll take a look. Now that I have all these specs passing, I need to start looking at cleaning up some of my duplication and adding in the new bits for this. It's about to get pretty crazy! 😂

… avoid methods like initialize being defined on Operation that shouldn't be defined on SaveOperation. SaveOperations now have attributes with passing specs
…me callbacks. May integrate them again if I figure out a cleaner way to register which callbacks an operation gets
jwoertink added a commit that referenced this pull request Sep 23, 2020
@jwoertink jwoertink changed the title [WIP] Operation Refactor [WIP] Operation Refactor Sandbox Sep 23, 2020
@jwoertink jwoertink removed BREAKING CHANGE This will cause a breaking change clarify api Rename/remove/add something to make the API easier to understand labels Sep 24, 2020
jwoertink added a commit that referenced this pull request Oct 15, 2020
* Separating SaveOperation from Operation. This is part of a series of commits related to #369

* Added missing specs for Operation attributes to ensure Operation can use both attribute and file_attribute

* Update src/avram/needy_initializer.cr

Co-authored-by: Matthew McGarvey <matthewmcgarvey14@gmail.com>

* added some docs and updated Operation to ensure the FailedOperation is called

* Refactor callbacks to remove some of the duplication.

* Refactor run method to clean up some duplication. Also having the run bang method return the value instead of the operation to be consistent with SaveOperation bang methods

* ran ameba

Co-authored-by: Matthew McGarvey <matthewmcgarvey14@gmail.com>
@jkthorne
Copy link
Contributor

Will there need to be updates to the guides on the main website?

@jwoertink
Copy link
Member Author

@wontruefree yup luckyframework/website#444 got the issue already. I'm still making some changes to the operations, so even though I'm merging in stuff, I'm trying things out and going back to redo a few things as it gets a little more clear as to how this all should work.

@matthewmcgarvey
Copy link
Member

@jwoertink You think we can close out this PR? I have no idea how much of this has been implemented and if there are any issues that can be closed out that are listed.

@jwoertink
Copy link
Member Author

Ok, I don't think I need this anymore. Closing it out!

@jwoertink jwoertink closed this Dec 31, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment