#12 楼 @reus
因为 Python 中的 foo 跟 foo() 不是一个东西,所以用 Python 写起来更自然。
对方法的引用 Python 是 foo,而 Ruby 是 self.method(:foo),其间并没有另外创建或者包装
,因为这个 method object 实际就存在,只是通过一种很不 python 的方式取 (既 self.method(:foo))。
同样,调用一个作为参数的方法 Python 也更简单,加个括号foo()
就行了,Ruby 就只能傻傻的 call 了,但是我认为本质都是一样的,只是 python 的马甲 (语法糖) 更好看一些就是了。
不过话说回来,个人认为修饰器
不是很 Ruby way,如#6 楼所说,当你用 Ruby 怀念修饰器
的时候,肯定有别的更合适的方案,就算非要用修饰器
,自己写一个满足自己需求的也不是很难,一楼已经证明。
#3 楼 @reus Ruby method 可以是对象 http://www.ruby-doc.org/core-2.1.1/Method.html 顺便山寨一下 makeHtmlTag 那个例子
module Decrorator
def wrap method_name
WarppedObj.new method_name, self
end
class WarppedObj
def initialize method_name, context
@method_name = method_name
@context = context
context.class_eval do
@@_warped_counter ||= {}
@@_warped_counter[method_name] ||= 0
end
end
def with method_name, *args
warpped_method = @method_name
@context.class_eval do
@@_warped_counter[warpped_method] += 1
alias_method :"#{@@_warped_counter[warpped_method]}_#{warpped_method}", warpped_method
i = @@_warped_counter[warpped_method]
define_method warpped_method do |*_args, &_blk|
ret = __send__ :"#{i}_#{warpped_method}", *_args, &_blk
__send__(method_name, *args) {ret}
end
end
end
end
end
class HTMLHelper
extend Decrorator
def makeHtmlTag tag, opts
"<#{tag} #{opts.map {|key, value| %Q{#{key}="#{value}"}}.join(' ')}>" + yield + "</#{tag}>"
end
def hello
'hello world'
end
wrap(:hello).with :makeHtmlTag, 'i', class: 'italic_css'
wrap(:hello).with :makeHtmlTag, 'b', class: 'bold_css'
end
puts HTMLHelper.new.hello # <b class="bold_css"><i class="italic_css">hello world</i></b>
log.group_by {|l| l[:time]}.values.map {|arr| arr.inject &:merge}
Module.class_variables + Module.class_variable_set
vagrant 的 box 跟你启动的 VM 好比类跟实例的关系。你新项目的 VM 跟你以前的 VM 是同一个 box 类的两个实例,所以你第一个 VM 装东西不会出现在第二个 VM 上。
vagrant package --output xxxx.box
可以把你配置好的虚机导出来,然后用你导出来的 box 启一个 vm。
attr_accessor :password
相等于定义两个方法:
def password
@password
end
def password= p
@password = p
end
如果用password = xxxx
只是给一个局部变量赋值,self.password = xxxx
才是调用第二个方法。password_salt 同理。如果单用password
,那样不会有歧义,所以不需要加 self。
#3 楼 @engin123456789 顺手写了一下: 完整的 CheckAttr Module:
module CheckAttr
def self.included base
base.extend ClassModule
base.class_eval do
def self.method_added method_name
if @_last_modified_method != method_name && method_name.to_s !~ /^_check_attr_/ && method_name.to_s !~ /=$/
@_last_modified_method = method_name
class_eval do
alias_method :"_check_attr_hacked_#{method_name}", method_name
define_method method_name do |*args, &blk|
ret = __send__(:"_check_attr_hacked_#{method_name}", *args, &blk)
instance_variables.each do |v|
method_name = :"#{v.to_s.gsub('@', '')}="
__send__(method_name, instance_variable_get(v)) if respond_to? method_name
end
return ret
end
end
end
end
end
end
class InvalidAttributeError < Exception; end
module ClassModule
def attr_checked attr, &validate_block
define_method "#{attr}=" do |value|
raise InvalidAttributeError.new("@#{attr}") unless validate_block.call value
instance_variable_set "@#{attr}",value
end
define_method attr do
instance_variable_get "@#{attr}"
end
end
end
end
Usage:
class Person
include CheckAttr
attr_checked :age do |v|
v >= 18
end
attr_checked(:weight) {|v| v >= 40}
def initialize name,age, weight
@name = name
@age = age
@weight = weight
end
def lose_weight w
@weight -= w
end
end
#person1 = Person.new 'shiyj',16, 50 # raise: InvalidAttributeError: @age
person2 = Person.new 'Fat Guy', 35, 200
person2.lose_weight 180 # raise InvalidAttributeError: @weight
大体就这思路,你可以再调整一下。 又或者你改一下你的思路,做成显示的验证,比如在执行 save 一类的时候验证,会方便高效许多。
#3 楼 @engin123456789 每个方法都加一遍验证一点都不过,我上面的代码稍微改动一下就行了,用 method_added 的钩子,自动 hack 每一个新加的方法,完成一遍验证。
不知道有没有@age=的时候能触发的 hook,不过根据你的需求可以实现在 initialize 的时候验证
module CheckAttr
def self.included base
base.extend ClassModule
base.class_eval do
def self.method_added method_name
if method_name == :initialize && !@_mark_
class_eval do
alias_method :original_initialize, :initialize
@_mark_ = true
def initialize *args, &blk
original_initialize *args, &blk
instance_variables.each do |v|
method_name = :"#{v.to_s.gsub('@', '')}="
__send__(method_name, instance_variable_get(v)) if respond_to? method_name
end
end
end
end
end
end
end
准确的说应该是&会调用后面的东西的 to_proc 方法。
&:upcase 等于:upcase.to_proc,可以参考一下 Symbol#to_proc
#8 楼 @u1378130755 这段代码跟p = Proc.new { puts "hello test" ; return}
等价,有什么问题么?
鼠标不好用……经常风怒
#3 楼 @u1378130755 p.call 是在这个 block 的作用域里执行 block 里面的代码,p 的作用域跟 test1 方法的作用域相同,所以里面的 return 跟在方法里 return 一样的。
#3 楼 @u1378130755 p.call 是在这个 block 的作用域里执行 block 里面的代码,p 的作用域跟 test1 方法的作用域相同,所以里面的 return 跟在方法里 return 一样的。
#3 楼 @u1378130755 这只是建了一个 Proc 对象,没执行 return,直到 call 了才会执行。
要用 block 里返回值要用 next,不是 return。
def generate_table operator
1.upto(9) do |i|
i.upto(9) {|j| print "#{i} #{operator} #{j} = #{i.send(operator, j)}\t"}
print "\n"
end
end
generate_table :+
generate_table :-
generate_table :*
:"xxx"
相当于"xxx".to_sym
,就像&:+
相当于:+.to_proc
#4 楼 @fbsender
这里面的@@a是属于顶层Object类的类变量
只是现象,不知道是什么原因。
class A
@@a = "It is @@a"
def get_a
puts @@a
end
end
obj = A.new
obj.get_a
obj.singleton_class.class_eval do
# 如果这里相当于Object.class_eval的话,为什么下面的get_a_singleton只属于obj?
@@a = 1
def get_a_singleton
puts @@a
end
end
obj.get_a_singleton
不过 singleton_class 作为一个只有一个实例匿名的类想不出 class variable 能有什么作用,所以从使用的角度来说这种场景应该不会出现。
因为前面的@@a 是 class A 的类变量,class << obj 里面的@@a 是 obj 的 singleton class 里的@@a,加上
obj.singleton_class.class_eval do
@@a = 1
end
这个@@a 才是 obj.get_a_singleton 能读到的@@a
只学 ruby 的话很简单。要学 ror 的话,纯 ruby 的只是能占 40% 么……
ETL 工具都带这些功能吧