Reputation: 13
On my journey to learn ruby & rails I went on and installed 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. 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:
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.
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.
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
Upvotes: 1
Views: 617
Reputation: 781
This is just 1 suggestion out of many possibilities.
You can use Ruby's meta programming abilities to dynamically send methods.
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.
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
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
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
Upvotes: 1