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

New Interface for Operations #469

Merged
merged 10 commits into from
Oct 15, 2020
Merged

New Interface for Operations #469

merged 10 commits into from
Oct 15, 2020

Conversation

jwoertink
Copy link
Member

Fixes #69

This PR is part of a series which breaks out #369 in to much smaller (and easier to digest) PRs. I'll try to best explain everything going on here.

In this PR SaveOperation no longer inherits from Operation, and Operation now has an interface which makes it feel a lot closer to how SaveOperation works. This means we can now do this:

class SignInUser < Avram::Operation
  needs http_context : HTTP::Context
  attribute email : String
  attribute password : String
  attribute remember_me : Bool
  file_attribute :biometric_retinal_scan

  before_run do
    check_login_count(http_context)
    validate_required email, password
  end

  after_run do |user|
    save_successful_login(user)
  end

  def run
    # do some fancy login logic
    new_current_user
  end
end

post "/login" do
  SignInUser.run(params, http_context: context) do |operation, user|
    if user
      store_user_and_redirect(user)
    else
       you_get_the_idea
    end
  end
end

All of the SaveOperation work exactly the same as before.

@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 Sep 23, 2020
@@ -24,6 +24,18 @@ module Avram::Callbacks
end
end

macro before_run(method_name)
Copy link
Member Author

Choose a reason for hiding this comment

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

This does the exact same thing as before_save, but it feels weird calling before_save in an Operation. To clean this up, we could just have before_run call before_save internally... but then if we ever needed before_save to act different, we'd end up having to break them out again.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is some tricky naming. What about before_execution?

Copy link
Member Author

Choose a reason for hiding this comment

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

The method that you call in these is called run. So like LoginUser.run(params). This was actually sort of modeled after the mutations gem in Ruby 😅 (though, that doesn't have callbacks).

Copy link
Member

Choose a reason for hiding this comment

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

I think I'd flip it. So before_save calls before_run under the hood. That way if before_save needs to change something we can modify just before_save and leave before_run alone

@@ -47,6 +59,18 @@ module Avram::Callbacks
end
end

macro before_run
Copy link
Member Author

Choose a reason for hiding this comment

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

ditto

# end
# end
# ```
macro after_run(&block)
Copy link
Member Author

Choose a reason for hiding this comment

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

The after_save equivalent doesn't exist yet, but will in a following PR to keep this as small as I can.

@@ -0,0 +1,102 @@
module Avram::NeedyInitializer
Copy link
Member Author

Choose a reason for hiding this comment

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

This module covers everything the needy_initializer_with_save_methods does, but without the save methods since those are related to SaveOperation specifically. I made a separate one because I had a lot of trouble with the compiler trying to tell "if it's SaveOp, then call these macros, else skip them". It was easier to just break them apart.

end

class Avram::Operation
abstract class Avram::Operation
Copy link
Member Author

Choose a reason for hiding this comment

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

If we wanted an extra level of type-safety, we could make this a Generic, and ensure run returns T. 🤔 Some people may just want it to run a task and be void at the end though..


class Avram::Operation
abstract class Avram::Operation
include Avram::NeedyInitializer
include Avram::DefineAttribute
include Avram::Validations
include Avram::SaveOperationErrors
Copy link
Member Author

Choose a reason for hiding this comment

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

This isn't really SaveOperation specific. I could rename it, or that could come in a future PR I'm planning with some cleanup stuff.

Copy link
Member

Choose a reason for hiding this comment

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

Future PR sounds good! Avram::OperationsErrors seems like a good name

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.

I was nervous that you were going to try to merge the monster PR 🤣

src/avram/callbacks.cr Outdated Show resolved Hide resolved
src/avram/callbacks.cr Show resolved Hide resolved
src/avram/needy_initializer.cr Outdated Show resolved Hide resolved
macro inherit_needs
\{% if !@type.constant(:OPERATION_NEEDS) %}
OPERATION_NEEDS = [] of Nil
\{% end %}
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't seem necessary...

Copy link
Member Author

Choose a reason for hiding this comment

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

🤷 lol. I know it's bad, but I literally copied and just ripped out code I assumed wasn't needed. I guess I should actually read what it is doing 😂

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 took a look at this again, and without it, things start to explode, so I'm leaving it in for now.

@jwoertink
Copy link
Member Author

@confact and @stephendolan since both of you have Lucky apps out in the wild, would you mind taking a glance over this to see if something is missing? When you get a free moment, of course.

I'm mainly looking for examples of regular Avram::Operation you might have in your apps where the code is a bit messy and see if this would clean that up and allow you to do what you need still. Or if you see something that makes you say "if this got merged, my app would break with no way to fix it"... Thanks! 😄

src/avram/callbacks.cr Outdated Show resolved Hide resolved
src/avram/callbacks.cr Outdated Show resolved Hide resolved
src/avram/callbacks.cr Outdated Show resolved Hide resolved
src/avram/operation.cr Outdated Show resolved Hide resolved
@jwoertink
Copy link
Member Author

Thanks for the review @matthewmcgarvey! Those changes definitely help to make this code easier to read. If you have any ideas for more specs I can add in, let me know!

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.

Looks good 👍

@jwoertink
Copy link
Member Author

Sweet! I'm gonna merge and start prepping the next PR for this series.

@jwoertink jwoertink merged commit 69e61d6 into master Oct 15, 2020
@jwoertink jwoertink deleted the separate_save_operation branch October 15, 2020 19:16
Copy link
Member

@paulcsmith paulcsmith left a comment

Choose a reason for hiding this comment

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

This is really awesome!! Thank you

@@ -24,6 +24,18 @@ module Avram::Callbacks
end
end

macro before_run(method_name)
Copy link
Member

Choose a reason for hiding this comment

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

I think I'd flip it. So before_save calls before_run under the hood. That way if before_save needs to change something we can modify just before_save and leave before_run alone


class Avram::Operation
abstract class Avram::Operation
include Avram::NeedyInitializer
include Avram::DefineAttribute
include Avram::Validations
include Avram::SaveOperationErrors
Copy link
Member

Choose a reason for hiding this comment

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

Future PR sounds good! Avram::OperationsErrors seems like a good name

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BREAKING CHANGE This will cause a breaking change clarify api Rename/remove/add something to make the API easier to understand
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Method for working with VirtualForms more easily
4 participants