如何重构方法以降低 RuboCop 的 ABCsize
How to refactor method to lower RuboCop's ABCsize
在学习 ruby 和 rails 的过程中,我继续安装了 Rubocop。到目前为止,它对以 ruby 方式重构我的代码有很大帮助,但现在我认为我在这个无助的案例中碰壁了。鉴于以下创建新实体的方法,我正在寻找一种方法来重构它以使 Rubocop 停止对我大喊大叫:
- 行长
- 分配分支条件大小(当前为 26.02/15)
我目前唯一能想到的,除了禁用那些 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
编辑:
根据要求,我附上了从不同的 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
这只是众多可能性中的一种建议。
您可以使用 Ruby 的元编程能力来动态发送方法。
meta
对象的字段很容易分配给 user.store_informations
因为字段匹配 1 对 1。
store
对象也是可能的,但它不会那么简单。
您可以将字段移动到您的 class 定义中的数组:
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
然后你可以定义一个私有方法动态设置 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
那么您可以改为调用该方法:
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
编辑#2
关于使用不同 classes 的对象填充字段;
解决此问题的一种方法是定义一种方法,该方法根据商店的 class 为您分配字段。
但话又说回来,如果您有数千家不同的商店,这可能不是最佳选择。
class StoreA; end
class StoreB; end
class StoreC; end
然后:
# 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
在学习 ruby 和 rails 的过程中,我继续安装了 Rubocop。到目前为止,它对以 ruby 方式重构我的代码有很大帮助,但现在我认为我在这个无助的案例中碰壁了。鉴于以下创建新实体的方法,我正在寻找一种方法来重构它以使 Rubocop 停止对我大喊大叫:
- 行长
- 分配分支条件大小(当前为 26.02/15)
我目前唯一能想到的,除了禁用那些 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
编辑: 根据要求,我附上了从不同的 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
这只是众多可能性中的一种建议。
您可以使用 Ruby 的元编程能力来动态发送方法。
meta
对象的字段很容易分配给 user.store_informations
因为字段匹配 1 对 1。
store
对象也是可能的,但它不会那么简单。
您可以将字段移动到您的 class 定义中的数组:
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
然后你可以定义一个私有方法动态设置 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
那么您可以改为调用该方法:
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
编辑#2
关于使用不同 classes 的对象填充字段;
解决此问题的一种方法是定义一种方法,该方法根据商店的 class 为您分配字段。 但话又说回来,如果您有数千家不同的商店,这可能不是最佳选择。
class StoreA; end
class StoreB; end
class StoreC; end
然后:
# 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