Module#refine
method接受一个类和一个块并返回一个细化模块,所以我想我可以定义:
class Class
def include_refined(klass)
_refinement = Module.new do
include refine(klass) {
yield if block_given?
}
end
self.send :include, _refinement
end
end
Run Code Online (Sandbox Code Playgroud)
并且以下测试通过
class Base
def foo
"foo"
end
end
class Receiver
include_refined(Base) {
def foo
"refined " + super
end
}
end
describe Receiver do
it { should respond_to(:foo) }
its(:foo) { should eq("refined foo") }
end
Run Code Online (Sandbox Code Playgroud)
因此,使用细化,我可以将类转换为模块,动态改进其行为,并将其包含在其他类中.
在rb_mod_refine的C实现中 我们看到了
refinement = rb_module_new();
RCLASS_SET_SUPER(refinement, klass);
Run Code Online (Sandbox Code Playgroud)
这只是将精炼的超类设置为klass
复制模块中的类的实现吗?
Class#include_refined
?从改进中提取这个方面是否合理?在本地"修补"而不是使用"使用"开关激活优化?我正在尝试理解Ruby的改进功能,我遇到了一个我不理解的场景.
以此示例代码为例:
class Traveller
def what_are_you
puts "I'm a Backpacker"
end
def self.preferred_accommodation
puts "Hostels"
end
end
module Refinements
module Money
def what_are_you
puts "I'm a cashed-up hedonist!"
end
module ClassMethods
def preferred_accommodation
puts "Expensive Hotels"
end
end
def self.included(base)
base.extend ClassMethods
end
end
refine Traveller do
include Money
end
end
Run Code Online (Sandbox Code Playgroud)
现在,当我在REPL中执行此操作时:
Traveller.new.what_are_you # => I'm a Backpacker
Traveller.preferred_accommodation # => Hostels
using Refinements
Traveller.new.what_are_you # => I'm a cashed-up hedonist!
Traveller.preferred_accommodation # => Hostels (???)
Run Code Online (Sandbox Code Playgroud)
为什么#what_are_you
精致,但.preferred_accommodation …
我有一个使用Ruby 2.0的Rails 4项目.我已经定义了一些改进.把
<% using MyRefinements %>
Run Code Online (Sandbox Code Playgroud)
在视图文件的顶部导致错误"未定义的方法'使用'".
当我添加:
using MyRefinements
Run Code Online (Sandbox Code Playgroud)
在我的控制器的顶部(在类声明之上),我可以成功地使用控制器中的细化,但是如果我尝试在视图中使用它,我会收到"未定义的方法"错误.
谢谢!
这里有关于ruby中当前优化实现的很好的文档:http: //ruby-doc.org//core-2.2.0/doc/syntax/refinements_rdoc.html,但是有一些奇怪的极端情况.
首先,include module
正交using module
(一个包括模块的实例方法,而另一个包括激活细化).但是有一个技巧包括一个改进模块本身,请参阅
更好的方法将ruby类转换为模块而不是使用细化?.
def to_module(klass)
Module.new do
#note that we return the refinement module itself here
return refine(klass) {
yield if block_given?
}
end
end
class Base
def foo
"foo"
end
end
class Receiver
include to_module(Base) {
def foo
"refined " + super
end
}
end
Receiver.new.foo #=> "refined foo"
Run Code Online (Sandbox Code Playgroud)
奇怪的是,这个改进模块不能用using
!
m=to_module(Base) {}
m.class #=> Module
using m
#=>TypeError: wrong argument type Class (expected Module)
Run Code Online (Sandbox Code Playgroud)
因此,仅使用细化模块的封闭模块.其次我想使用上面的yield技巧来传递一个Proc来改进(即使它只接受一个块),而不是像在https://www.new-bamboo.co中 …
我正在尝试使用ruby refinements来应用rails hooks.
我想避免猴子补丁.当猴子修补它会这样工作
ActiveRecord::Base.class_eval do
after_find do
# do something with
my_method
end
def my_method
# something useful
end
end
Run Code Online (Sandbox Code Playgroud)
通过这样的方式,我已经能够拥有类方法:
module ActiveRecordRefinements
refine ActiveRecord::Base.singleton_class do
def my_method
#something cool
end
end
end
Run Code Online (Sandbox Code Playgroud)
但我无法逃脱.我尝试过使用self.used(klass)
但似乎无法正确使用语法.
欢迎任何帮助.
谢谢.
我试图在IRB中使用Refinement(v0.9.6,Ruby 2.3.0):
module Foo
refine Object do
def foo() "foo" end
end
end
using Foo # => RuntimeError: main.using is permitted only at toplevel
Run Code Online (Sandbox Code Playgroud)
这基本上是文档中的确切设置(导致相同的错误).
什么地方出了错?我该如何解决?
精化是对v2.0的实验性补充,然后在v2.1中进行了修改并永久化.它提供了一种通过提供"在本地扩展类的方法"来避免"猴子修补"的方法.
我试图申请Refinements
到这个最近的问题,我将因此简化:
a = [[1, "a"],
[2, "b"],
[3, "c"],
[4, "d"]]
b = [[1, "AA"],
[2, "B"],
[3, "C"],
[5, "D"]]
Run Code Online (Sandbox Code Playgroud)
在位置的元素i
中a
的元素相匹配的偏移i
的b
,如果:
a[i].first == b[i].first
Run Code Online (Sandbox Code Playgroud)
和
a[i].last.downcase == b[i].last.downcase
Run Code Online (Sandbox Code Playgroud)
换句话说,字符串的匹配与大小写无关.
问题是确定a
匹配相应元素的元素数量b
.我们看到答案是两个,即抵消的元素1
和2
.
一种方法是使用Monkey-patch String#==:
class String
alias :dbl_eql :==
def ==(other)
downcase.dbl_eql(other.downcase)
end
end
a.zip(b).count { |ae,be| ae.zip(be).all? { |aee,bee| aee==bee } }
#=> 2
Run Code Online (Sandbox Code Playgroud)
或者改为使用Refinements
: …
它似乎to_proc
不适用于优化中定义的方法:
module ArrayExtensions
refine Array do
def sum
reduce(0, :+)
end
end
end
using ArrayExtensions
puts [[1, 2, 3]].map { |array| array.sum } # => 6
puts [[1, 2, 3]].map(&:sum) # => array.rb:13:in `map': undefined method `sum' for [1, 2, 3]:Array (NoMethodError)
puts [1, 2, 3].method(:sum).to_proc.call # => array.rb:14:in `method': undefined method `sum' for class `Array' (NameError)
Run Code Online (Sandbox Code Playgroud)
这是预期的行为吗?有解决方法吗?
在第2章" 改进 "部分的元编程Ruby 2中,我发现了以下Ruby代码:
class MyClass
def my_method
"original my_method()"
end
def another_method
my_method
end
end
module MyClassRefinement
refine MyClass do
def my_method
"refined my_method()"
end
end
end
using MyClassRefinement
MyClass.new.my_method # => "refined my_method()"
MyClass.new.another_method # => "original my_method()" - How is this possible?
Run Code Online (Sandbox Code Playgroud)
据作者说:
但是,调用
another_method
可能抓住你措手不及:即使你打电话another_method
后using
,调用my_method
本身发生之前using
-所以它调用该方法的原始的,未精制的版本.
这完全让我失望.
为什么MyClass.new.another_method
打印"原始my_method()",因为它使用后using MyClassRefinement
,作者试图在这里说什么?
任何人都可以提供更直观/更好的解释吗?
谢谢.
假设我正在设计一种特定于领域的语言。对于这个简化的示例,我们有变量、数字以及将事物添加在一起的能力(变量或数字)
class Variable
attr_reader :name
def initialize(name)
@name = name
end
end
class Addition
attr_reader :lhs, :rhs
def initialize(lhs, rhs)
@lhs = lhs
@rhs = rhs
end
end
Run Code Online (Sandbox Code Playgroud)
现在,如果我想表示表达式x + 1
,我会写Addition.new(Variable.new('x'), 1)
。
我们可以通过+
在 上提供一种方法来使这更方便Variable
。
class Variable
def +(other)
Addition.new(self, other)
end
end
Run Code Online (Sandbox Code Playgroud)
然后我们就可以写了Variable.new('x') + 1
。
但现在,假设我想要相反的结果:1 + x
。显然,我不想进行猴子补丁Integer#+
,因为这会永久禁用普通的 Ruby 整数加法。我认为这将是一个很好的改进用例。
具体来说,我想定义一个方法expr
,该方法采用一个块并在重新定义的上下文中评估该块+
以构造我的 DSL 实例。也就是说,我想要类似的东西
module Context
refine Integer do
def +(other)
Addition.new(self, other) …
Run Code Online (Sandbox Code Playgroud)