PragTob/after_do

Apply after and before using a yml file

Closed this issue · 7 comments

I have been using after_do in a project and we thought of a nice way to apply "aspects" to many classes in a non-intrusive way.

The main idea is to write a .yml file like this:

aspects:
  - name: Logging
    klass: LoggingAspect
    description: This aspects provides basic method logging
    advices:
      - name: method_log
        targets:
          - klass: Bundle
            target_methods:
              - initialize

There is a Loader class that reads this file and applies the rules defined at LoggingAspect#method_log to Bundle#initialize. This config can be expanded to apply many other "advices" to many other methods.

Would you guys be interested in incorporating this kind of functionality?
I would gladly send you a PR =).

Otherwise, is it Ok for me to create another gem (say, after_do_rails) that bundles this configuration together for rails apps?

Thank you!

Hi there!

Thanks for your issue report/input :)

So It looks like a nice idea! It also reminded me that I basically left no/few hints as to how one can write aspects and use them.

May I ask why you configure it in a yml file instead of ruby code? Just out of curiosity to see how people use it.

Personally I mostly build ruby data structures. E.g. a Hash that has klass names as keys and an array of method names as values, which are the classes/values to which I'll apply the aspect. Like this:

{Common::Toggle => [:toggle],
Image => [:update_image],
::Shoes::Common::Style => [:update_style],
::Shoes::Common::Remove => [:remove],
::Shoes::Slot => [:mouse_hovered, :mouse_left],
::Shoes::TextBlock => [:replace]}

A small helper method for this is:

def after_every(hash, &blk)
  hash.each {|klass, methods| klass.after methods, &blk }
end

Do you think it might be worth to make that part of the gem?

As for the yaml idea, I'm unsure about the name after_do_rails as currently I don't see what the YAML loading has to do with rails but I'd be happy about a gem that does this. As I want to keep after_do itself very small and nimble I think that right now I wouldn't want to integrate loading from a YAML file. There probably is logic to that that might very well belong into the gem - feedback welcome :)

Cheers and thanks!
Tobi

Aw, right.

We have been using after_do to log some selected method's invocation. We inject things in the Rails logger (tags and stuff) based on the method context, that's why the name.

We prefer not to add the configuration to each class individually in order to make the class' code clean, with no references to log formatting and things as such. We could configure everything using a hash (which is what YAML.load returns), but we prefer to let the configuration in the .yml file just to keep things easy to find. (If you wanna check aspect's configuration, you just check aspects.yml)

We really do not want to mix in modules for logging. We want your code to be completely ignorant of the aspects.
The main advantage of the .yml approach is that we can change the logging configuration without the need to generate a new deploy. In our process, changing ruby code involves debian packaging and other not-so-easy stuff.

Your method of configuration is quite analogous to ours. The only difference is that the "block" you pass is a instance method of the specific Aspect class being used.

Anyway, I think giving people some hints on the README.md file about how to write the dynamic configuration would be quite nice.

I understand your point about keeping after_do simple and slim, and I think this is the right choice. The 'loading' part can quite well be incorporated in another gem that includes after_do as a dependency.

Then, I think we will end up coding the loader gem (whose name is yet tbd) =).

Thank you for the awesome work!

Hey,

thanks for the feedback! :-) Also now I understand the reasoning much better and it sounds like an awesome thing to have!

Sounds good! Yes we also don't mix in/extend the objects. They also don't know that an aspect hooks into them. I got a method that goes and extends all the classes on which we need after_do.

E.g.

  def extend_needed_classes
    affected_classes.each {|klass| klass.extend AfterDo}
  end

  def affected_classes
    classes = NEED_TO_UPDATE.keys +
              NEED_TO_ASYNC_UPDATE_GUI.keys +
              NEED_CONTENTS_ALIGNMENT.keys +
              SAME_POSITION.keys +
              CHANGED_POSITION.keys
    classes.uniq
  end

Please let me know (tag me, comment, email, whatever) if I can be of help with the gem/stuff that you think would benefit more like this so we can put it in the after_do gem. :-) Also let me know when the first version is online so I can have a look!

Cheers,
Tobi

Nice!
I will probably get to work on this next week. As soon as I have something functional I will let you know =).

Thank you so much for attention 👍

Cheers,

Renan

Hi,

@stupied4ever and I have created two gems for the use case I described above:
https://github.com/rranelli/after_do-loader
https://github.com/stupied4ever/after_do-logging

The first one is responsible for defining a .yml format, loading, and applying the callbacks.
The second one is concerned with the logging itself.

Both are still a "draft", and it would be really nice to receive feedback/comments =). (I think we still have to sort class names and stuff like that.)

Cheers,
Renan

Hi @rranelli ! Sounds awesome will give it a read :-)

However, might take me some time. I just arrived in Barcelona today for Barcelona Ruby conference so will see when I get the time. :-)

Cheers,
Tobi

Sorry for getting back so late, looking at the two gems back in time reminded me that how they use after_do is vastly different from how I use it, which is great that it allows it. I won't introduce some abstraction and yml configuration is also better to live in separate gems :)

So closing here now.