Enumerable#find ifnone
I have a WidgetKitManager
who needs to make sure that WidgetKit
's have homogenous widget types. When adding a widget, it should either be added to a widget_kit with it's family or a new widget_kit should be created for it to live alone:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | describe WidgetKitManager do describe '#widget_kit_for_new_widget' do it 'returns a widget_kit with the same type as the new widget' do user = create(:user) expected_widget_kit = create( :widget_kit, :with_complete_widgets, user: user ) new_widget = build( :widget, date_package: expected_widget_kit.widgets.first.date_package ) widget_kit_manager = WidgetKitManager.new(user.reload) expect(widget_kit_manager.widget_kit_for_new_widget(new_widget)) .to eq(expected_widget_kit) end it 'creates a new widget_kit if no suitable widget_kit exists' do user = create(:user) new_widget = build( :widget, :widget_with_required_associations ) widget_kit_manager = WidgetKitManager.new(user.reload) expect { widget_kit_manager.widget_kit_for_new_widget(new_widget) } .to change { user.widget_kits.count }.by(1) end it 'adds the new widget to the widget_kit' do user = create(:user) new_widget = build( :widget, :widget_with_required_associations ) widget_kit_manager = WidgetKitManager.new(user.reload) widget_kit = widget_kit_manager.widget_kit_for_new_widget(new_widget) expect(widget_kit.widgets).to include(new_widget) end end end |
When implementing WidgetKitManager#widget_kit_for_new_widget
I noticed a nice feature in Enumerable#find
, the optional ifnone argument:
find(ifnone = nil) { |obj| block } → obj or nil
Passes each entry in enum to block. Returns the first for which block is not false. If no object matches, calls ifnone and returns its result when it is specified, or returns nil otherwise.
I don't know why, but until today my eyes just glossed over that little optional argument detail. Conveniently I noticed today and it was just what I needed! Look through a collection finding a match to some condition, if you don't find anything do some other business instead. Awesome:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | class WidgetKitManager attr_accessor :current_user delegate :widget_kits, to: :current_user def initialize(current_user) self.current_user = current_user end def widget_kit_for_new_widget(widget) found_kit = widget_kits.detect(-> { create_widget_kit(widget) }) do |kit| kit.type_id == widget.type_id end found_kit.widgets << widget found_kit end ... private def create_widget_kit(widget) current_user.widget_kits.create( type_id: widget.type_id ) end end |