自己在项目中测试了一下,却没有顺利执行,如下
class User < ApplicationRecord
alias :real_avatar :avatar
def avatar
real_avatar.present? ? "real_avatar" : "default.png"
end
end
控制器执行的结果: User 实例:
2.4.2 (main):0 > user = User.last
User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> #<User:0x00007fde39a372c0
id: 2,
uid: "017526292153702",
avatar: "http://img2.imgtn.bdimg.com/it/u=3563567972,3348399262&fm=27&gp=0.jpg",
nickname: "Nick-member",
created_at: Fri, 19 Jan 2018 00:26:16 CST +08:00,
updated_at: Fri, 19 Jan 2018 00:26:16 CST +08:00>
调用的 atatar
, 并没有展示我想要的 "real_avatar"
, 而是报了错误
user.avatar
=> NameError: undefined method `avatar' for class `#<Class:0x00007fe4db80b6d8>'
哪里出了错了吗?
顺序
def avatar
real_avatar.present? ? "real_avatar" : "default.png"
end
alias :real_avatar :avatar
user model:
class User < ApplicationRecord
def avatar
real_avatar.present? ? "real_avatar" : "default.png"
end
alias :real_avatar :avatar
end
执行结果:
2.4.2 (main):0 > user = User.last
User Load (0.6ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> #<User:0x00007fd3cc9330a8
id: 2,
uid: "017526292153702",
avatar: "http://img2.imgtn.bdimg.com/it/u=3563567972,3348399262&fm=27&gp=0.jpg",
nickname: "Nick-member",
created_at: Fri, 19 Jan 2018 00:26:16 CST +08:00,
updated_at: Fri, 19 Jan 2018 00:26:16 CST +08:00>
2.4.2 (main):0 > user.avatar
SystemStackError: stack level too deep
from /Users/dubing/workspace/vcooline/group_buying_front/app/models/user.rb:18:in `avatar'
2.4.2 (main):0 >
例子的意思是 length 本来 String 里面就有定义,书中的意思是你假如别名这个方法的话它还是会引用之前的那个。但这里 avatar 在 User 或 ApplicationRecord 里面本来是没有定义的,它调用了你后期定义导致了无限嵌套。
你要是想实现默认头像的效果可参考以下代码:
class User < ApplicationRecord
attr_accessor :real_avatar
def avatar
real_avatar || "default.png"
end
end
你的定义顺序没有错,但在 Rails 模型中,我认为,你应该用 alias_attribute
alias_attribute :real_avatar, :avatar
附,其实你完全可不必用 alias 的方法来重写 avatar,以达到有默认头像的功能。 你可以这样:
class User < ApplicationRecord
def avatar
read_attribute_before_type_cast["avatar"] || "default.png"
end
end
你这里只是使用了 attr_accessor
, 接着...
这个是为了,有些人没有上传头像,才会调用默认头像, 可是你这种就会,一直使用默认头像,毕竟 real_avatar
始终是空。
原因在于,ActiveRecord
方法是动态加载的,它自己加上了很多元编程的东西,其实在你运行alias :real_avatar :avatar
的时候,还不存在avatar
这个实例方法,不信你试试看
class User < ApplicationRecord
p instance_methods(false)
alias :real_avatar :avatar
def avatar
real_avatar.present? ? "real_avatar" : "default.png"
end
end
你看 Ruby 元编程的话,建议用原生 Ruby 来尝试例子,后面它会讲到 Rails 里面的元编程,你心里就有数了
再来一个例子
class Test
p instance_methods(false)
def t
end
p instance_methods(false)
alias :new_t :t
p instance_methods(false)
end
一般插件里面也会有默认选项,如 Peperclip:
has_attached_file :avatar, :styles => { :thumb => "60x60#", :small => "120x120>",
:medium => "180x180" },
# default avatar goes here
:default_url => '/system/default/user_avatar.png',
:path => ...,
:url => ...
书上的例子是打开了String
类,打开之前,length
方法已经被定义了,所以可以alias
,而User
中的 avatar 方法是动态定义的,好像是你第一次调用的时候才定义的。
像大家说的 avatar 是动态定义的,很难保证执行顺序,所以现在一般用 prepend 取代 alias 的作用
class User < ApplicationRecord
module AvatarWithDefaultValue
def avatar
super || 'default value'
end
end
prepend AvatarWithDefaultValue
end
我在处理的时候是这样写的
class User < ApplicationRecord
avatar
super || 'default value'
end
end
你的写法,比我的优雅了太多了。相形见绌。