Skip to content

Use code generation for inlining #150

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

Closed
robnormal opened this issue May 17, 2015 · 11 comments · Fixed by #159
Closed

Use code generation for inlining #150

robnormal opened this issue May 17, 2015 · 11 comments · Fixed by #159

Comments

@robnormal
Copy link
Contributor

The Contract class has been heavily inlined, with results in much of the code being closed for modification. I cannot subclass Contract and redefine how it handles Class contracts, for example, without rewriting an entirely new make_validator method.

Why not generate contracts.rb with ERB or other templating solution instead? That would allow the code to be broken down as it should be for maintainability, without sacrificing performance. (In particular, it would allow extending or monkeypatching Contract to play nicely with RSpec, rather than modifying RSpec itself, as in contracts-rspec.)

@nixpulvis
Copy link
Contributor

So, the user would need to "compile" their own contracts.rb every time?

Are there other projects anyone knows about doing something like this?

@robnormal
Copy link
Contributor Author

Only a user extending or changing it would need to regenerate the file. A standard user (which would presumably include all current users) could leave it as it is. Also, it need not be generated statically. Generating the needed code and eval-ing it would work as well. Since it would only occur once per process, the performance impact may be small.

@waterlink
Copy link
Collaborator

Thanks @robnormal. We already thought about using some sort of meta-code for Contract class, because it is not really manageable with such big methods. We haven't thought about allowing users to re-define some parts of it though..

Actually, it could be generated both statically and dynamically:

  • When user don't care and want to use default behavior -> use static built file shipped with gem
  • When user do care and has some changes and specifically tells contracts to use them -> generate on fly and load generated Contract class

I have already thought about having it in some sort of DSL language in form of YML. But ERB could be much much simpler. WDYT?

@robnormal
Copy link
Contributor Author

Sounds good to me. I might fork and start work on this now, as I'd like to use this gem in an existing team project, but the need to rewrite every RSpec test is a show-stopper.

@egonSchiele
Copy link
Owner

If you do that, please send a PR! I'd like to see what it looks like.

@robnormal
Copy link
Contributor Author

Definitely!

@waterlink
Copy link
Collaborator

My draft proposal is this: https://github.com./waterlink/wateruby#inline-method-call

Add to this ability to dynamically inject and override anything from this hash before compilation to ruby and you get your extensibility with everything inlined.

Feel free to challenge or throw any other ideas onto the board

@robnormal
Copy link
Contributor Author

Suppose I wanted to create SubContract < Contract and define SubContract.proc_contract to override the one in the YAML file. How would I go about that?

@waterlink
Copy link
Collaborator

definitions:
  SubContract:
    define: class <%= name %> < Contract
    derive_from: Contract
    definitions:
      self.proc_contract:
        # e.g. lambda { true }
        inlinable: true
        define: def <%= name %>(contract, its_klass)
        pre: its_klass == Proc
        body: |
          # your custom body here

inlining mechanisms take care about tracking method dependency tree, so when you change one of the inlinable methods, all its users (the methods where it is inlined) will be generated for your subclass.

@egonSchiele
Copy link
Owner

@waterlink, check out this comment. I have the same issue with the YAML approach...it adds extra complexity.

Maybe we could have some kind of hooks interface, so you could write:

Contract.override(:class) do |val|
  ...body of new check
end

@alex-fedorov
Copy link
Collaborator

@egonSchiele Ah, exactly what I was thinking for some days already :)

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

Successfully merging a pull request may close this issue.

5 participants