[英]How to refactor method to lower RuboCop's ABCsize
On my journey to learn ruby & rails I went on and installed Rubocop.在我学习 ruby 和 rails 的过程中,我继续安装了 Rubocop。 So far it's been a great help in refactoring my code the ruby-way, but now I think I've hit the wall with this helpless case.
到目前为止,它对以 ruby 方式重构我的代码有很大帮助,但现在我想我已经被这个无助的案例撞到了墙角。 Given the following method for creating a new entity, I'm looking for a way to refactor it to make Rubocop stop yelling at me about:
鉴于以下创建新实体的方法,我正在寻找一种重构它的方法,以使 Rubocop 停止对我大喊大叫:
the only thing that I can think of for the moment, except of disabling those cops ofc, is actually splitting up the model into two smaller ones (say basic info and financial) and set them up accordingly, but I get the impression that this would move the complexity out of the creation method and put it elsewhere, as I would need to remember to create both related entities etc. Any hints are more than welcome.我目前唯一能想到的,除了禁用那些 cops ofc,实际上是将模型分成两个较小的模型(比如基本信息和财务)并相应地设置它们,但我的印象是这会将复杂性从创建方法中移出并将其放在其他地方,因为我需要记住创建两个相关实体等。任何提示都非常受欢迎。
def create_store_information(store, meta)
user = @datasource.user
user.store_informations.create!(
name: store['name'],
description: store['description'],
status: 1,
url: store['URL'].downcase,
store_version: store['version'],
api_version: store['wc_version'],
timezone: meta['timezone'],
currency: meta['currency'],
currency_format: meta['currency_format'],
currency_position: meta['currency_position'],
thousand_separator: meta['thousand_separator'],
decimal_separator: meta['decimal_separator'],
price_num_decimals: meta['price_num_decimals'],
tax_included: cast_to_bool(meta['tax_included']),
weight_unit: meta['weight_unit'],
dimension_unit: meta['dimension_unit'],
ssl_enabled: cast_to_bool(meta['ssl_enabled']),
permalinks_enabled: cast_to_bool(meta['permalinks_enabled']),
generate_password: cast_to_bool(meta['generate_password']),
user: user
)
end
Edit: As per request, I'm attaching a second sample of creating store_information from a different class.编辑:根据请求,我附上了从不同的类创建 store_information 的第二个示例。
def create_store_information(store, meta)
user = @datasource.user
user.store_informations.create!(
name: store['id'],
description: store['name'],
status: 1,
url: store['domain'].downcase,
store_version: '1.0',
api_version: '1.0',
timezone: meta['timezone'],
currency: meta['currency'],
currency_format: meta['money_format'],
currency_position: '', # not applicable
thousand_separator: '', # not applicable, take from user's locale
decimal_separator: '', # not applicable, take from user's locale
price_num_decimals: '', # not applicable, take from user's locale
tax_included: cast_to_bool(meta['taxes_included']),
weight_unit: nil, # not applicable
dimension_unit: nil, # not applicable
ssl_enabled: cast_to_bool(meta['force_ssl']),
permalinks_enabled: true,
generate_password: false,
user: user
)
end
This is just 1 suggestion out of many possibilities.这只是众多可能性中的 1 个建议。
You can use Ruby's meta programming abilities to dynamically send methods.您可以使用 Ruby 的元编程能力来动态发送方法。 The
meta
object's fields are easy to assign the user.store_informations
because the fields match 1 for 1. It is also possible for the store
object but it wouldn't be as straightforward. meta
对象的字段很容易分配给user.store_informations
因为字段匹配 1 为 1。 store
对象也是可能的,但它不会那么简单。
You can move the fields to an array inside your class definition:您可以将字段移动到类定义中的数组:
CAST_TO_BOOL = %w(
tax_included
ssl_enabled
permalinks_enabled
generate_password
).freeze
META_FIELDS = %w(
timezone
currency
currency_format
currency_position
thousand_separator
decimal_separator
price_num_decimals
tax_included
weight_unit
dimension_unit
ssl_enabled
permalinks_enabled
generate_password
).freeze
then you could define a private method which dynamically sets the meta
fields of the user.store_informations
那么你可以定义一个私有方法来动态设置
user.store_informations
的meta
字段
private
def set_meta_fields_to_store_information(user)
META_FIELDS.each do |field|
if CAST_TO_BOOL.include? field
user.store_informations.__send__ "#{f}=" { cast_to_bool( meta[field] ) }
next
end
user.store_informations.__send__ "#{f}=" { meta[field] }
end
end
then you could call that method instead:那么你可以调用该方法:
def create_store_information(store, meta)
user = @datasource.user
user.store_informations.new(
name: store['name'],
description: store['description'],
status: 1,
url: store['URL'].downcase,
store_version: store['version'],
api_version: store['wc_version'],
user: user
)
set_meta_fields_to_store_information(user)
user.save!
end
Edit#2编辑#2
Regarding populating the fields with objects of different classes;关于用不同类的对象填充字段;
One way to go about it would be to define a method which assign's the fields for you depending on the class of the store.一种方法是定义一个方法,该方法根据商店的类为您分配字段。 But then again, if you have thousands of different stores, this probably wouldn't be optimal.
但话又说回来,如果您有数千家不同的商店,这可能不是最佳选择。
class StoreA; end
class StoreB; end
class StoreC; end
then:然后:
# you could also use dynamic method dispatching here instead:
def set_store_information_to_user(store, user)
case store
when StoreA
assign_store_a_method(store, user)
when StoreB
assign_store_b_method(store, user)
when StoreC
assign_store_c_method(store, user)
end
end
private
def assign_store_a_method(store, user); end
def assign_store_b_method(store, user); end
def assign_store_c_method(store, user); end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.