Search code examples
ruby-on-railssingle-table-inheritance

Rails STI: transfer records from one model to another


I have a model called Coupons Then I have two child models CouponApplicationsand ApprovedCoupons. The last two inherit from Couponsvia an STI architecture.

Now I want to realise the following:

  1. A user sees CouponApplications
  2. He clicks on an Approve button which makes the CouponApplications ApprovedCoupons

I realise that I could simply update the typecolumn of the Couponsrecord to change types. However, there are several Concerns, hooks etc in the ApprovedCoupons model which are happening after creation so this is not so easy. In fact, I want to create a complete new record to trigger those Concerns, hooks etc.

So I wrote this which I consider really bad:

@coupon_application = CouponApplication.find(params[:id])
@approved_coupon = ApprovedCoupon.new

# copy/paste attributes except the ID as this would be considered a duplication
@approved_coupon.attributes = @coupon_application.attributes.except("id")

# set the new type
@approved_coupon.update_attributes(type: "Advertisement")

@approved_coupon.save

I hope you understand what I want to achieve. It works this way but I doubt this is clean code.

To summarize:

  • I want to change the Coupontype from CouponApplication to ApprovedCoupon
  • I still want to trigger the Concerns, hooks etc. in my ApprovedCoupon model so I decided to create a new ApprovedCoupon record instead of just changing the type.

Is there any better approach?


Solution

  • You could add an approve method to your CouponApplication model like this:

    class CouponApplication < Coupon
      ...
    
      def approve
        data = attributes.except('id', 'created_at', 'updated_at')
        ApprovedCoupon.create(data.merge(type: 'Advertisement'))
      end
    end
    

    Now your code can be simplified to this:

    @coupon_application = CouponApplication.find(params[:id])
    @approved_coupon = @coupon_application.approve