I'm trying to use jruby as a script language (for writing quests for a game), but I need to prevent usage of potentially dangerous classes like Dir, Process or RubyVM.
I already found a cumbersome way to blacklist single methods and classes:
class Dir
def Dir.[]
end
def Dir.chdir
end
def Dir.chroot
end
def Dir.delete
end
def Dir.entries
end
def Dir.exist?
end
def Dir.exists?
end
def Dir.foreach
end
def Dir.getwd
end
def Dir.glob
end
def Dir.home
end
def Dir.mkdir
end
def Dir.initialitze
end
def Dir.open
end
def Dir.pwd
end
def Dir.rmdir
end
def Dir.unlink
end
def close
end
def each
end
def fileno
end
def inspect
end
def path
end
def pos
end
def read
end
def rewind
end
def seek
end
def tell
end
def to_path
end
end
But I really hope that there is a much easier way to perform this task or even better whitelist the classes that should be able to use.
You can just make specific methods private:
class Dir
private :close, :each ....
private_class_method :glob, :home ...
end
Or you can undefine whole class (bad idea as for me):
Object.send(:remove_const, :Dir)
Or remove methods (also bad idea to remove methods from Ruby core class):
class Dir
remove_method :close, :each ....
end
Related
I've got an existing library comprised of many services which all respond to the method execute each method does it's logic
class BaseService
def execute
raise NotImplementedError
end
end
class CreateUserService < BaseService
def execute
# Some code that does Other stuff
end
end
class NotifyService < BaseService
def execute
# Some code that does other other stuff
end
end
I would like to do something to accomplish something like:
class BaseService
around :execute do |&block|
puts 'Before'
block.call
puts 'After'
end
end
Which then wraps every execute method including child classes so that logic can be performed before and after.
I've done something like this:
module Hooks
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def around(*symbols, &block)
to_prepend = build_module(symbols) do |*args, &mblock|
result = nil
block.call do
result = super(*args, &mblock)
end
result
end
prepend to_prepend
end
private
def build_module(symbols, &block)
Module.new do
symbols.each do |symbol|
define_method(symbol, &block)
end
end
end
end
end
class BaseService
include Hooks
around :execute do |&block|
puts 'before'
block.call
puts 'after'
end
# ..
end
However when the around method only gets executed on the base class. I'm guessing this is due to the nature of prepend. The ancestral order looks like:
[<Module>, BaseService, Hooks, ...]
[NotifyService, <Module>, BaseService, Hooks, ...]
etc
Is there a way I can accomplish this?
Thank you!
You don't modify child classes, and you don't call super from their execute methods.
As far as I can tell, there's no reason why CreateUserService#execute should call the wrapped BaseService#execute.
One way to achieve what you want are refinements:
class BaseService
def execute
p "BaseService#execute"
end
end
class CreateUserService < BaseService
def execute
p "CreateUserService#execute"
end
end
class NotifyService < BaseService
def execute
p "NotifyService#execute"
end
end
module WrappedExecute
[NotifyService, CreateUserService, BaseService].each do |service_class|
refine service_class do
def execute
puts "Before"
super
puts "After"
end
end
end
end
CreateUserService.new.execute
#=> "CreateUserService#execute"
using WrappedExecute
CreateUserService.new.execute
# Before
# "CreateUserService#execute"
# After
Note:
to_prepend = build_module(symbols) do |*args, &mblock|
result = nil
block.call do
result = super(*args, &mblock)
end
result
end
could be replaced by
to_prepend = build_module(symbols) do |*args, &mblock|
block.call do
super(*args, &mblock)
end
end
You'd still need to include Hooks in every Service class, though.
What I've ended up doing is something that I'm unsure whether I'm ok with or not.
class BaseService
include Hooks
def self.inherited(subclass)
subclass.around(:execute) do |&block|
Rails.logger.tagged(tags) do
block.call
end
end
end
end
This enabled me to apply to all other classes the around functionality to avoid a massive refactor. Not sure if this is the best solution but wanted to post for others to see.
module Gym
def self.included(class_or_module)
class_or_module.send(:include, InstanceMethods)
class_or_module.extend(ClassMethods)
end
module ClassMethods
def build
end
end
module InstanceMethods
def open
end
def book_for_practice
end
def close
end
end
end
this is an example in the Ruby's Object Lifecycle Callbacks section of RubyMonk. I don't understand how it's supposed to work or what the point of this is. self.included should just document how the two modules within Gym get used, right? why does class_or_module then get sent/extended? why doesn't it get saved in some sort of arrays that document the lifecyle, like in the examples leading up to this one, such as
##extended_objects = []
def self.extended_objects
##extended_objects
end
def self.extended(class_or_module)
##extended_objects << class_or_module
It's not just documentation. self.included is a callback method that gets called as soon as the module is being included in any other module or class.
Instance methods are included via send, class or module methods via extend in that example.
Find out more in the Ruby documentation.
Let me answer your Question One by one.
1: self.included should just document how the two modules within Gym get used ?
module A
def instance_methods_1
p 'hello instance_methods_1'
end
def instance_methods_2
p 'hello instance_methods_2'
end
module KlassMethods
def klass_methods_1
p 'Hello!! klass_methods_1'
end
def klass_methods_2
p 'Hello!! klass_methods_2'
end
end
end
class B
include A # instead of writing two piece of code like this we could wrap in one using `self.included` method Hook
extend A::KlassMethods
end
B.new.instance_methods_1
B.new.instance_methods_2
B.klass_methods_1
B.klass_methods_2
Another version of same program with method hook using self.included
module A
# this is special method one of the methods hook in ruby.
def self.included(base)
base.extend(KlassMethods)
end
def instance_methods_1
p 'hello instance_methods_1'
end
def instance_methods_2
p 'hello instance_methods_2'
end
module KlassMethods
def klass_methods_1
p 'Hello!! klass_methods_1'
end
def klass_methods_2
p 'Hello!! klass_methods_2'
end
end
end
class B
include A
end
B.new.instance_methods_1
B.new.instance_methods_2
B.klass_methods_1
B.klass_methods_2
2: self.included
To know more about included hook and
Ruby Hook
3: why does class_or_module then get sent/extended ?
module AddAdditionalProperty
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def add_additional_property
p 'ClassMethods::add_additional_property'
end
end
end
module ActiveRecord
class Base
def test
p 'Base test Method'
end
end
end
ActiveRecord::Base.send(:include, AddAdditionalProperty)
ActiveRecord::Base.add_additional_property
## Another version of same Program
module AddAdditionalProperty
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def add_additional_property
p 'ClassMethods::add_additional_property'
end
end
end
module ActiveRecord
class Base
include AddAdditionalProperty
def test
p 'Base test Method'
end
end
end
ActiveRecord::Base.add_additional_property
Hope this answer help you !!!
I'm trying to create a method that passes the caller as the default last argument. According to this, I only need:
class A
def initialize(object = self)
# work with object
end
end
so that in:
class B
def initialize
A.new # self is a B instance here
end
end
self will be B rather than A;
However, this doesn't seem to work. Here's some test code:
class A
def self.test test, t=self
puts t
end
end
class B
def test test,t=self
puts t
end
end
class T
def a
A.test 'hey'
end
def b
B.new.test 'hey'
end
def self.a
A.test 'hey'
end
def self.b
B.new.test'hey'
end
end
and I get:
T.new.a # => A
T.new.b # => #<B:0x000000015fef00>
T.a # => A
T.b # => #<B:0x000000015fed98>
whereas I expect it to be T or #<T:0x000000015fdf08>. Is there a way to set the default last argument to the caller?
EDIT:
class Registry
class << self
def add(component, base=self)
self.send(component).update( base.to_s.split('::').last => base)
end
end
end
The idea is pretty simple, you would use it like this
class Asset_Manager
Registry.add :utilities
end
and you access it like:
include Registry.utilities 'Debugger'
I'm trying to de-couple classes by having a middle-man management type class that takes care of inter-class communications, auto-loading of missing classes and erroring when it doesn't exist, it works but I just want to be able to use the above rather than:
class Asset_Manager
Registry.add :utilities, self
end
It just feels cleaner, that and I wanted to know if such a thing was possible.
You can't escape the explicit self. But you can hide it with some ruby magic.
class Registry
def self.add(group, klass)
puts "registering #{klass} in #{group}"
end
end
module Registrable
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def register_in(group)
Registry.add(group, self)
end
end
end
class AssetManager
include Registrable
register_in :utilities
end
# >> registering AssetManager in utilities
In short, you can't.
Ruby resolves the default arguments in the context of the receiver. That is, the object before the . in a method call. What you called the receiver should be the caller, actually.
class A
def test1(value = a)
puts a
end
def test2(value = b)
puts b
end
def a
"a"
end
end
a = A.new
a.test1 #=> a
def a.b; "b" end
a.test2 #=> b
If I were you, I would use the extended (or included) hook, where both the extending class and the extended module can be accessed. You can program what ever logic you want based on the information.
module Registry
module Utilities
def self.extended(cls)
#puts cls
::Registry.send(component).update( cls.to_s.split('::').last => cls)
end
end
end
class Asset_Manager
extend Registry::Utilities
end
This my implementation to developing way to run code before all method in your model
The call "before_hook :months_used" method need to be on bottom of class to the ExecutionHooks can get the instance_method loaded in the module. I would like to load the instance methods on top
class BalanceChart < BalanceFind
include ExecutionHooks
attr_reader :options
def initialize(options = {})
#options = options
#begin_at = #options[:begin_at]
end
def months_used
range.map{|date| I18n.l date, format: :month_year}.uniq!
end
before_hook :months_used
end
module ExecutionHooks
def self.included(base)
base.send :extend, ClassMethods
end
module ClassMethods
def before
#hooks.each do |name|
m = instance_method(name)
define_method(name) do |*args, &block|
return if #begin_at.blank? ## the code you can execute before methods
m.bind(self).(*args, &block) ## your old code in the method of the class
end
end
end
def before_hook(*method_name)
#hooks = method_name
before
end
def hooks
#hooks ||= []
end
end
end
You can do this with prepend. prepend is like include in that it adds a module to the ancestors of the class, however instead of adding it after the class it adds it before.
This means that if a method exists both in the prepended module and the class then the module implementation is called first (and it can optionally call super if it wants to call the base class).
This allows you to write a hooks module like so:
module Hooks
def before(*method_names)
to_prepend = Module.new do
method_names.each do |name|
define_method(name) do |*args, &block|
puts "before #{name}"
super(*args,&block)
end
end
end
prepend to_prepend
end
end
class Example
extend Hooks
before :foo, :bar
def foo
puts "in foo"
end
def bar
puts "in bar"
end
end
In real use you would probably want to stash that module somewhere so that each call to before doesn't create a new module but that is just an inplementation detail
#rathrio This is my implementation using method_added that you talked. Thanks
module ExecutionHooks
def validation
p "works1"
end
def self.included(base)
base.send :extend, ClassMethods
end
end
module ClassMethods
attr_writer :hooked
def hooked
#hooked ||= []
end
def method_added(method)
return if #hooks.nil?
return unless #hooks.include?(method)
m = self.instance_method(method)
unless hooked.include?(method)
hooked << method
define_method(method) do |*args, &block|
validation
m.bind(self).(*args, &block) ## your old code in the method of the class
end
end
end
def before_hook(*method_name)
#hooks = method_name
end
def hooks
#hooks ||= []
end
end
end
class BalanceChart < BalanceFind
include ExecutionHooks
before_hook :months_data, :months_used, :debits_amount, :test
def test
"test"
end
end
Instead of redefining the method when calling before_hook, you could override the method_added hook to prepend your before hooks to a method right after it was defined. This way your before_hook calls can be (actually, must be) placed at the top of the class definition.
I'm writing a module in Ruby 1.9.2 that defines several methods. When any of these methods is called, I want each of them to execute a certain statement first.
module MyModule
def go_forth
a re-used statement
# code particular to this method follows ...
end
def and_multiply
a re-used statement
# then something completely different ...
end
end
But I want to avoid putting that a re-used statement code explicitly in every single method. Is there a way to do so?
(If it matters, a re-used statement will have each method, when called, print its own name. It will do so via some variant of puts __method__.)
Like this:
module M
def self.before(*names)
names.each do |name|
m = instance_method(name)
define_method(name) do |*args, &block|
yield
m.bind(self).(*args, &block)
end
end
end
end
module M
def hello
puts "yo"
end
def bye
puts "bum"
end
before(*instance_methods) { puts "start" }
end
class C
include M
end
C.new.bye #=> "start" "bum"
C.new.hello #=> "start" "yo"
This is exactly what aspector is created for.
With aspector you don't need to write the boilerplate metaprogramming code. You can even go one step further to extract the common logic into a separate aspect class and test it independently.
require 'aspector'
module MyModule
aspector do
before :go_forth, :add_multiply do
...
end
end
def go_forth
# code particular to this method follows ...
end
def and_multiply
# then something completely different ...
end
end
You can implement it with method_missing through proxy Module, like this:
module MyModule
module MyRealModule
def self.go_forth
puts "it works!"
# code particular to this method follows ...
end
def self.and_multiply
puts "it works!"
# then something completely different ...
end
end
def self.method_missing(m, *args, &block)
reused_statement
if MyModule::MyRealModule.methods.include?( m.to_s )
MyModule::MyRealModule.send(m)
else
super
end
end
def self.reused_statement
puts "reused statement"
end
end
MyModule.go_forth
#=> it works!
MyModule.stop_forth
#=> NoMethodError...
You can do this by metaprogramming technique, here's an example:
module YourModule
def included(mod)
def mod.method_added(name)
return if #added
#added = true
original_method = "original #{name}"
alias_method original_method, name
define_method(name) do |*args|
reused_statement
result = send original_method, *args
puts "The method #{name} called!"
result
end
#added = false
end
end
def reused_statement
end
end
module MyModule
include YourModule
def go_forth
end
def and_multiply
end
end
works only in ruby 1.9 and higher
UPDATE: and also can't use block, i.e. no yield in instance methods
I dunno, why I was downvoted - but a proper AOP framework is better than meta-programming hackery. And thats what OP was trying to achieve.
http://debasishg.blogspot.com/2006/06/does-ruby-need-aop.html
Another Solution could be:
module Aop
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def before_filter(method_name, options = {})
aop_methods = Array(options[:only]).compact
return if aop_methods.empty?
aop_methods.each do |m|
alias_method "#{m}_old", m
class_eval <<-RUBY,__FILE__,__LINE__ + 1
def #{m}
#{method_name}
#{m}_old
end
RUBY
end
end
end
end
module Bar
def hello
puts "Running hello world"
end
end
class Foo
include Bar
def find_hello
puts "Running find hello"
end
include Aop
before_filter :find_hello, :only => :hello
end
a = Foo.new()
a.hello()
It is possible with meta-programming.
Another alternative is Aquarium. Aquarium is a framework that implements Aspect-Oriented Programming (AOP) for Ruby. AOP allow you to implement functionality across normal object and method boundaries. Your use case, applying a pre-action on every method, is a basic task of AOP.