facets icon indicating copy to clipboard operation
facets copied to clipboard

Idea for a scheme to help keep common code separate while allowing both ...

Open anithri opened this issue 10 years ago • 8 comments

...refinements and patching.

See the README for more complete explanation.

anithri avatar Feb 12 '15 00:02 anithri

Please have a look at this: https://github.com/rubyworks/reusing/blob/master/README.md

I have thought about this quite a bit, and tend to think that having to maintain two sets of identical methods just to deal with Ruby's boiler plate for refinements is a lot to ask in terms of additional maintenance. And having to do so in a cherry-pickable way, with one module for every method, is even more of a nightmare. My hope is that Matz will eventually see that refinements, as they are, need to be normalized somehow, perhaps as described in the above link. In the meantime perhaps the Reusing project can be improved to cover more of the edge cases and thus become a viable option. What do you think?

trans avatar Feb 12 '15 19:02 trans

Had a look, I understand what the issues are, and I had another idea or two. I don't have time to turn this into working code tonight, but I'll do so sometime in next couple of days.

filename =  ... #insert code to determine filename 
core_class = 'string' #insert code to determine core class
feature = 'snakecase' #insert code to determine module name
# create appropriate Module
m = create_module_from(core_class, feature) #> Facets::String::Snakecase
# read feature file into module to isolate class
m.module_eval(File.read(filename)) # reads file into a namespace
#now you have Facets::String::Snakecase::String with a snakecase method
m.refine(String){include Facets::String::Snakecase::String}
# and then elsewhere

using Facets::String::Snakecase
'TestMeNow'.snakecase #> test_me_now

for bonus points we use Module.const_missing? and we can automate almost the whole thing. No gsub on code files or executing arbitrary code in root namesapce.

I tried out the important bits (module_eval and refine) in irb and they seemed to work as expected.

anithri avatar Feb 13 '15 06:02 anithri

Cool. Let me know what you come up with. It would be nice if we could control the module used. So the user can have one module per method, or use one module for multiple methods -- a way to cherry pick into a module.

trans avatar Feb 15 '15 14:02 trans

Not sure if this is related or not, but I actually wanted to post an issue in relation to refinements. I lately started using the Corefines gem and it had a non-invasive approach to patching that I was hoping all facets could have something similar to it. The gem's readme page covers well the approach and the use cases.

SandNerd avatar Jul 30 '16 20:07 SandNerd

I think this is an interesting idea, but it would probably require a major refactor of how facets works. Do you think it's worth it?

Also, what are the performance implication of refinements? Is it 0?

Finally, is it supported by any other ruby? JRuby? Rubinius?

ioquatix avatar Mar 09 '17 08:03 ioquatix

Any more thoughts/progress on this, or refinements in general? Are Refinements still the preferred way forward in the Ruby community for monkey patching?

Shouldn't we update Facets to use Refinements instead of global monkey patches?? (Even if we don't support both ways, seems like we should at least support the "better" way... Oh right, compatibility with other Rubies. Has that improved yet?)

Corefines looks cool, has a nice using Corefines::Object::in? API ... but doesn't have nearly as many extensions as Facets has. Can we combine the best of this 2 libraries?

TylerRick avatar Jul 21 '20 17:07 TylerRick

A long time ago, before refinements even existed, I introduced an approach similar to String.include(Facets::String::foo). I don't recall exactly now, l think I even used a method called using for this, but back then it was still monkey-patching. It seemed reasonable to me at the time and I thought it was the way forward -- certainly would have made supporting refinements easier now -- but I got major push back on the idea and abandoned it.

I still think the best approach would be for Ruby to adopt something like what I suggest in "Thoughts" in the Reusing lib README, but for whatever reason Matz either doesn't agree, or just hasn't given any attention to this. In any case it doesn't look like it will change anytime soon so...

What we can do is modularize all the code and then create separate monkey-patch files and refinement files that include these modules. There are some limitations to this however. Modules can't be refined, so all module facets won't apply. It also might cause a small performance hit. I know ActiveSpport put everything in modules (does it still?) so maybe it's an acceptable hit?

trans avatar Jul 25 '20 14:07 trans

I think you can use Module#prepend. If you wanted to make separate files, you can make one which is the module, and one which prepends it.

The alternative is refinements.

ioquatix avatar Jul 25 '20 23:07 ioquatix