简体   繁体   English

如何重构方法以降低 RuboCop 的 ABCsize

[英]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 停止对我大喊大叫:

  1. Line length线长
  2. Assignment Branch Condition size (currently 26.02/15)分配分支条件大小(当前为 26.02/15)

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_informationsmeta字段

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM