binding-ninja-0.2.3/0000755000175100017510000000000013627157535013267 5ustar pravipravibinding-ninja-0.2.3/.gitignore0000644000175100017510000000032013627157535015252 0ustar pravipravi/.bundle/ /.yardoc /Gemfile.lock /_yardoc/ /coverage/ /doc/ /pkg/ /spec/reports/ /tmp/ *.bundle *.so *.o *.a mkmf.log lib/binding_ninja/binding_ninja.jar # rspec failure tracking .rspec_status .ruby-version binding-ninja-0.2.3/Rakefile0000644000175100017510000000102113627157535014726 0ustar pravipravirequire "bundler/gem_tasks" require "rspec/core/rake_task" RSpec::Core::RakeTask.new(:spec) task :build => :compile case RUBY_PLATFORM when /java/ require 'rake/javaextensiontask' Rake::JavaExtensionTask.new('binding_ninja') do |ext| ext.lib_dir = "lib/binding_ninja" ext.source_version = '1.8' ext.target_version = '1.8' end else require 'rake/extensiontask' Rake::ExtensionTask.new("binding_ninja") do |ext| ext.lib_dir = "lib/binding_ninja" end end task :default => [:clobber, :compile, :spec] binding-ninja-0.2.3/benchmark.rb0000644000175100017510000000115513627157535015550 0ustar pravipravirequire "benchmark/ips" require "binding_ninja" require "binding_of_caller" class Foo extend BindingNinja auto_inject_binding def foo1(b) b.local_variables end def foo2 binding.of_caller(1).local_variables end def foo3(b) b.local_variables end auto_inject_binding :foo3, if: :enable_auto_inject_binding? def enable_auto_inject_binding? true end end foo = Foo.new p foo.foo1 p foo.foo2 p foo.foo3 Benchmark.ips do |x| x.report("binding_ninja") { foo.foo1 } x.report("binding_ninja_with_condition") { foo.foo3 } x.report("binding_of_caller") { foo.foo2 } x.compare! end binding-ninja-0.2.3/ext/0000755000175100017510000000000013627157535014067 5ustar pravipravibinding-ninja-0.2.3/ext/binding_ninja/0000755000175100017510000000000013627157535016660 5ustar pravipravibinding-ninja-0.2.3/ext/binding_ninja/binding_ninja.c0000644000175100017510000000772213627157535021625 0ustar pravipravi#include "binding_ninja.h" VALUE rb_mBindingNinja; static VALUE auto_inject_binding_invoke(int argc, VALUE *argv, VALUE self); static VALUE auto_inject_binding_invoke_without_cond(int argc, VALUE *argv, VALUE self); static VALUE auto_inject_binding_invoke_stub(int argc, VALUE *argv, VALUE self); static ID options_id; static VALUE auto_inject_binding(int argc, VALUE *argv, VALUE mod) { ID mid; static ID extensions_id; static ID keyword_ids[1]; VALUE extensions, ext_mod, method_sym, options, opt, cond; cond = Qundef; if (!keyword_ids[0]) { keyword_ids[0] = rb_intern("if"); } if (!extensions_id) { extensions_id = rb_intern("@auto_inject_binding_extensions"); } if (rb_ivar_defined(mod, options_id)) { options = rb_ivar_get(mod, options_id); } else { options = rb_hash_new(); rb_ivar_set(mod, options_id, options); } rb_scan_args(argc, argv, "1:", &method_sym, &opt); if (!NIL_P(opt)) { rb_get_kwargs(opt, keyword_ids, 0, 1, &cond); if (cond != Qundef) { rb_hash_aset(options, method_sym, cond); } } mid = SYM2ID(method_sym); if (abs(rb_mod_method_arity(mod, mid)) < 1) { rb_raise(rb_eArgError, "target method receives 1 or more arguments"); } extensions = rb_ivar_get(rb_mBindingNinja, extensions_id); ext_mod = rb_hash_aref(extensions, mod); if (NIL_P(ext_mod)) { ext_mod = rb_module_new(); rb_hash_aset(extensions, mod, ext_mod); } if (rb_mod_include_p(mod, ext_mod) == Qfalse) { rb_prepend_module(mod, ext_mod); } if (cond == Qundef) { rb_define_method_id(ext_mod, mid, auto_inject_binding_invoke_without_cond, -1); } else if (rb_obj_is_proc(cond) || SYMBOL_P(cond)) { rb_define_method_id(ext_mod, mid, auto_inject_binding_invoke, -1); } else { if (RTEST(cond)) { rb_define_method_id(ext_mod, mid, auto_inject_binding_invoke_without_cond, -1); } else { rb_define_method_id(ext_mod, mid, auto_inject_binding_invoke_stub, -1); } } return method_sym; } static VALUE auto_inject_binding_invoke(int argc, VALUE *argv, VALUE self) { VALUE method_sym, ext_mod, options, binding, args_ary, cond; static VALUE dummy_proc_args, dummy_method_arg[0]; if (!dummy_proc_args) { dummy_proc_args = rb_ary_new(); rb_obj_freeze(dummy_proc_args); } method_sym = ID2SYM(rb_frame_this_func()); options = rb_funcall(CLASS_OF(self), rb_intern("auto_inject_binding_options"), 0); cond = rb_hash_lookup2(options, method_sym, Qtrue); if (rb_obj_is_proc(cond)) { if (abs(rb_proc_arity(cond)) > 0) { cond = rb_proc_call(cond, rb_ary_new_from_args(1, self)); } else { cond = rb_proc_call(cond, dummy_proc_args); } } else if (SYMBOL_P(cond)) { cond = rb_method_call(0, dummy_method_arg, rb_obj_method(self, cond)); } args_ary = rb_ary_new_from_values(argc, argv); if (RTEST(cond)) { binding = rb_binding_new(); rb_ary_unshift(args_ary, binding); } else { rb_ary_unshift(args_ary, Qnil); } return rb_call_super(argc + 1, RARRAY_CONST_PTR(args_ary)); } static VALUE auto_inject_binding_invoke_without_cond(int argc, VALUE *argv, VALUE self) { VALUE args_ary, binding; args_ary = rb_ary_new_from_values(argc, argv); binding = rb_binding_new(); rb_ary_unshift(args_ary, binding); return rb_call_super(argc + 1, RARRAY_CONST_PTR(args_ary)); } static VALUE auto_inject_binding_invoke_stub(int argc, VALUE *argv, VALUE self) { VALUE args_ary; args_ary = rb_ary_new_from_values(argc, argv); rb_ary_unshift(args_ary, Qnil); return rb_call_super(argc + 1, RARRAY_CONST_PTR(args_ary)); } void Init_binding_ninja(void) { rb_mBindingNinja = rb_define_module("BindingNinja"); options_id = rb_intern("@auto_inject_binding_options"); rb_ivar_set(rb_mBindingNinja, rb_intern("@auto_inject_binding_extensions"), rb_hash_new()); rb_define_private_method(rb_mBindingNinja, "auto_inject_binding", auto_inject_binding, -1); } binding-ninja-0.2.3/ext/binding_ninja/RubyBindingNinja.java0000644000175100017510000001516513627157535022727 0ustar pravipravipackage io.github.joker1007; import java.util.stream.Stream; import org.jruby.anno.JRubyMethod; import org.jruby.anno.JRubyModule; import org.jruby.ast.util.ArgsUtil; import org.jruby.internal.runtime.methods.JavaMethod; import org.jruby.Ruby; import org.jruby.RubyBasicObject; import org.jruby.RubyBinding; import org.jruby.RubyClass; import org.jruby.RubyHash; import org.jruby.RubyMethod; import org.jruby.RubyModule; import org.jruby.RubyProc; import org.jruby.RubySymbol; import org.jruby.runtime.Block; import org.jruby.runtime.Constants; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.Helpers; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.Visibility; @JRubyModule(name = "BindingNinja") public class RubyBindingNinja { static String OPTIONS_ID = "@auto_inject_binding_options"; static String EXTENSIONS_ID = "@auto_inject_binding_extensions"; static Integer[] jrubyVersionNums = Stream.of(Constants.VERSION.split("\\.")).map(Integer::parseInt).toArray(Integer[]::new); @JRubyMethod(name = "auto_inject_binding", module = true, visibility = Visibility.PRIVATE, required = 1, optional = 1) public static IRubyObject autoInjectBinding(ThreadContext context, IRubyObject recv, IRubyObject[] args) { final Ruby runtime = context.getRuntime(); final IRubyObject extensions = runtime.getModule("BindingNinja").getInstanceVariable(EXTENSIONS_ID); final IRubyObject methodSym = args[0]; IRubyObject ivar = ((RubyModule)recv).getInstanceVariable(OPTIONS_ID); final IRubyObject options; if (ivar.isTrue()) { options = ivar; } else { options = RubyHash.newHash(runtime); ((RubyModule)recv).setInstanceVariable(OPTIONS_ID, options); } IRubyObject extModTmp = extensions instanceof RubyHash ? ((RubyHash)extensions).op_aref(context, recv) : context.nil; if (extModTmp.isNil()) { extModTmp = RubyModule.newModule(runtime); ((RubyHash)extensions).op_aset(context, recv, extModTmp); } final RubyModule extMod = (RubyModule)extModTmp; if (!((RubyModule)recv).hasModuleInHierarchy(extMod)) { ((RubyModule)recv).prependModule(extMod); } IRubyObject cond = RubyBasicObject.UNDEF; if (args.length == 2) { final RubyHash kwArgs = args[1].convertToHash(); final IRubyObject[] rets = ArgsUtil.extractKeywordArgs(runtime.getCurrentContext(), kwArgs, "if"); cond = rets[0]; } if (cond != RubyBasicObject.UNDEF) { ((RubyHash)options).op_aset(context, methodSym, cond); } final String mid = methodSym.asJavaString(); if (cond == RubyBasicObject.UNDEF) { extMod.addMethod(mid, autoInjectBindingInvokeWithoutCond(extMod, mid)); } else { final RubyClass klass = cond.getMetaClass().getRealClass(); if (klass == runtime.getProc() || klass == runtime.getSymbol()) { extMod.addMethod(mid, autoInjectBindingInvoke(extMod, mid)); } else if (cond.isTrue()) { extMod.addMethod(mid, autoInjectBindingInvokeWithoutCond(extMod, mid)); } else { extMod.addMethod(mid, autoInjectBindingInvokeStub(extMod, mid)); } } return methodSym; } private static JavaMethod autoInjectBindingInvoke(final RubyModule extMod, String name) { return new JavaMethod(extMod, Visibility.PUBLIC, name) { @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) { final IRubyObject options = Helpers.invoke(context, self.getMetaClass(), "auto_inject_binding_options"); IRubyObject cond = ((org.jruby.RubyHash)options).op_aref(context, RubySymbol.newSymbol(context.getRuntime(), name)); final RubyClass klass = cond.getMetaClass().getRealClass(); if (klass == context.getRuntime().getProc()) { if (Math.abs(((RubyProc)cond).arity().getIntValue()) > 0) { cond = ((RubyProc)cond).call(context, new IRubyObject[]{self}); } else { cond = ((RubyProc)cond).call(context, new IRubyObject[]{}); } } else if (klass == context.getRuntime().getSymbol()) { final IRubyObject method = ((RubyBasicObject)self).method(cond); final IRubyObject proc = ((RubyMethod) method).to_proc(context); cond = ((RubyProc)proc).call(context, new IRubyObject[]{}); } final IRubyObject[] unshiftedArgs = new IRubyObject[args.length + 1]; if (cond.isTrue()) { unshiftedArgs[0] = RubyBinding.newBinding(context.getRuntime(), context.currentBinding()); } else { unshiftedArgs[0] = context.nil; } System.arraycopy(args, 0, unshiftedArgs, 1, args.length); if (jrubyVersionNums[0] >= 9 && jrubyVersionNums[1] >= 2 && jrubyVersionNums[2] >= 7) { return Helpers.invokeSuper(context, self, clazz, name, unshiftedArgs, block); } else { return Helpers.invokeSuper(context, self, extMod, name, unshiftedArgs, block); } } }; } private static JavaMethod autoInjectBindingInvokeStub(final RubyModule extMod, String name) { return new JavaMethod(extMod, Visibility.PUBLIC, name) { @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) { final IRubyObject[] unshiftedArgs = new IRubyObject[args.length + 1]; unshiftedArgs[0] = context.nil; System.arraycopy(args, 0, unshiftedArgs, 1, args.length); if (jrubyVersionNums[0] >= 9 && jrubyVersionNums[1] >= 2 && jrubyVersionNums[2] >= 7) { return Helpers.invokeSuper(context, self, clazz, name, unshiftedArgs, block); } else { return Helpers.invokeSuper(context, self, extMod, name, unshiftedArgs, block); } } }; } private static JavaMethod autoInjectBindingInvokeWithoutCond(final RubyModule extMod, String name) { return new JavaMethod(extMod, Visibility.PUBLIC, name) { @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) { final IRubyObject[] unshiftedArgs = new IRubyObject[args.length + 1]; unshiftedArgs[0] = RubyBinding.newBinding(context.getRuntime(), context.currentBinding()); System.arraycopy(args, 0, unshiftedArgs, 1, args.length); if (jrubyVersionNums[0] >= 9 && jrubyVersionNums[1] >= 2 && jrubyVersionNums[2] >= 7) { return Helpers.invokeSuper(context, self, clazz, name, unshiftedArgs, block); } else { return Helpers.invokeSuper(context, self, extMod, name, unshiftedArgs, block); } } }; } } binding-ninja-0.2.3/ext/binding_ninja/binding_ninja.h0000644000175100017510000000014313627157535021620 0ustar pravipravi#ifndef BINDING_NINJA_H #define BINDING_NINJA_H 1 #include "ruby.h" #endif /* BINDING_NINJA_H */ binding-ninja-0.2.3/ext/binding_ninja/BindingNinjaService.java0000644000175100017510000000112013627157535023370 0ustar pravipravipackage io.github.joker1007; import java.io.IOException; import org.jruby.Ruby; import org.jruby.RubyHash; import org.jruby.RubyModule; import org.jruby.runtime.load.BasicLibraryService; public class BindingNinjaService implements BasicLibraryService { @Override public boolean basicLoad(final Ruby runtime) throws IOException { RubyModule bindingNinja = runtime.defineModule("BindingNinja"); bindingNinja.setInstanceVariable("@auto_inject_binding_extensions", RubyHash.newHash(runtime)); bindingNinja.defineAnnotatedMethods(RubyBindingNinja.class); return true; } } binding-ninja-0.2.3/ext/binding_ninja/extconf.rb0000644000175100017510000000007713627157535020657 0ustar pravipravirequire "mkmf" create_makefile("binding_ninja/binding_ninja") binding-ninja-0.2.3/bin/0000755000175100017510000000000013627157535014037 5ustar pravipravibinding-ninja-0.2.3/bin/console0000755000175100017510000000053413627157535015431 0ustar pravipravi#!/usr/bin/env ruby require "bundler/setup" require "binding_ninja" # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. # (If you use this, don't forget to add pry to your Gemfile!) # require "pry" # Pry.start require "irb" IRB.start(__FILE__) binding-ninja-0.2.3/bin/setup0000755000175100017510000000020313627157535015120 0ustar pravipravi#!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' set -vx bundle install # Do any other automated setup that you need to do here binding-ninja-0.2.3/binding_ninja.gemspec0000644000175100017510000000233313627157535017426 0ustar pravipravi# coding: utf-8 lib = File.expand_path("../lib", __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "binding_ninja/version" Gem::Specification.new do |spec| spec.name = "binding_ninja" spec.version = BindingNinja::VERSION spec.authors = ["joker1007"] spec.email = ["kakyoin.hierophant@gmail.com"] spec.summary = %q{pass binding of method caller implicitly} spec.description = %q{pass binding of method caller implicitly} spec.homepage = "https://github.com/joker1007/binding_ninja" spec.license = "MIT" spec.files = `git ls-files -z`.split("\x0").reject do |f| f.match(%r{^(test|spec|features)/}) end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] case RUBY_PLATFORM when /java/ spec.platform = "java" spec.files << "lib/binding_ninja/binding_ninja.jar" else spec.extensions = ["ext/binding_ninja/extconf.rb"] end spec.add_development_dependency "bundler", ">= 1.15" spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "rake-compiler" spec.add_development_dependency "rspec", "~> 3.0" end binding-ninja-0.2.3/lib/0000755000175100017510000000000013627157535014035 5ustar pravipravibinding-ninja-0.2.3/lib/binding_ninja/0000755000175100017510000000000013627157535016626 5ustar pravipravibinding-ninja-0.2.3/lib/binding_ninja/version.rb0000644000175100017510000000005413627157535020637 0ustar pravipravimodule BindingNinja VERSION = "0.2.3" end binding-ninja-0.2.3/lib/binding_ninja.rb0000644000175100017510000000150713627157535017156 0ustar pravipravirequire "binding_ninja/version" if /java/ =~ RUBY_PLATFORM require_relative 'binding_ninja/binding_ninja.jar' Java::IoGithubJoker1007::BindingNinjaService.new.basicLoad(JRuby.runtime) else require "binding_ninja/binding_ninja" end module BindingNinja def auto_inject_binding_options {} end METHOD_DEFINER = ->(klass) do unless klass.method_defined?(:auto_inject_binding_options) options = {} klass.class_eval do @auto_inject_binding_options = options end klass.define_singleton_method(:auto_inject_binding_options) do super().merge(options) end end end def inherited(klass) super METHOD_DEFINER.call(klass) end def included(klass) super METHOD_DEFINER.call(klass) end def self.extended(klass) METHOD_DEFINER.call(klass) end end binding-ninja-0.2.3/README.md0000644000175100017510000000714713627157535014557 0ustar pravipravi# BindingNinja [![Gem Version](https://badge.fury.io/rb/binding_ninja.svg)](https://badge.fury.io/rb/binding_ninja) [![Build Status](https://travis-ci.org/joker1007/binding_ninja.svg?branch=master)](https://travis-ci.org/joker1007/binding_ninja) This is method wrapper for passing binding of method caller implcitly. And this is lightweight alternative of [binding_of_caller](https://github.com/banister/binding_of_caller) ## Installation Add this line to your application's Gemfile: ```ruby gem 'binding_ninja' ``` And then execute: $ bundle Or install it yourself as: $ gem install binding_ninja ## Usage ```ruby class Foo extend BindingNinja def foo(binding, arg1, arg2) p binding p arg1 p arg2 end auto_inject_binding :foo def foo2(binding, arg1, arg2) p binding p arg1 p arg2 end auto_inject_binding :foo2, if: ENV["ENABLE_BINDING_NINJA"] # or auto_inject_binding :foo2, if: ->(obj) { obj.enable_auto_inject_binding? } # or auto_inject_binding :foo2, if: :enable_auto_inject_binding? def enable_auto_inject_binding? true end end Foo.new.foo(1, 2) # => # => 1 # => 2 # if ENABLE_BINDING_NINJA environment variable is nil or false, # binding arguments is nil. Foo.new.foo2(1, 2) # => nil # => 1 # => 2 ``` `:if` option can accept Proc object and Symbol object. If option accepts a proc or symbol, uses result of evaluating the proc or method named by the symbol. ## Compare to binding_of_caller ```ruby require "benchmark/ips" require "binding_ninja" require "binding_of_caller" class Foo extend BindingNinja auto_inject_binding def foo1(b) b.local_variables end def foo2 binding.of_caller(1).local_variables end def foo3(b) b.local_variables end auto_inject_binding :foo3, if: :enable_auto_inject_binding? def enable_auto_inject_binding? true end end foo = Foo.new p foo.foo1 p foo.foo2 p foo.foo3 Benchmark.ips do |x| x.report("binding_ninja") { foo.foo1 } x.report("binding_ninja_with_condition") { foo.foo3 } x.report("binding_of_caller") { foo.foo2 } x.compare! end ``` ``` Warming up -------------------------------------- binding_ninja 106.598k i/100ms binding_ninja_with_condition 49.660k i/100ms binding_of_caller 6.799k i/100ms Calculating ------------------------------------- binding_ninja 1.351M (± 0.4%) i/s - 6.822M in 5.051283s binding_ninja_with_condition 566.555k (± 0.3%) i/s - 2.880M in 5.083895s binding_of_caller 69.968k (± 0.8%) i/s - 353.548k in 5.053337s Comparison: binding_ninja: 1350619.1 i/s binding_ninja_with_condition: 566555.1 i/s - 2.38x slower binding_of_caller: 69968.2 i/s - 19.30x slower ``` 13x - 16x faster than binding_of_caller. And binding_ninja has very simple code base. ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/joker1007/binding_ninja. ## License The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). binding-ninja-0.2.3/.travis.yml0000644000175100017510000000020513627157535015375 0ustar pravipravisudo: false language: ruby rvm: - 2.5.5 - 2.6.3 - jruby-9.2.6.0 - jruby-9.2.7.0 before_install: gem install bundler -v 2.0.1 binding-ninja-0.2.3/Gemfile0000644000175100017510000000025013627157535014557 0ustar pravipravisource "https://rubygems.org" git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } # Specify your gem's dependencies in binding_ninja.gemspec gemspec binding-ninja-0.2.3/.rspec0000644000175100017510000000003713627157535014404 0ustar pravipravi--format documentation --color binding-ninja-0.2.3/LICENSE.txt0000644000175100017510000000206413627157535015114 0ustar pravipraviThe MIT License (MIT) Copyright (c) 2017 joker1007 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.