Home > Software engineering >  Rails caching using fetch
Rails caching using fetch

Time:10-08

Rails.cache.fetch(plan_list_cache_key(project), expires_in: EXPIRES_TIME) do
      plans = Plan.all.where(is_active: true)
      project_plan = project.project_plan
      hash_array = []
      plans.each do |plan|
        plan = plan.attributes
        expired = if (plan["id"] == project.plan.id)
                    (project_plan.end_date.to_date - Date.current).to_i.positive? ? false : true
                  else
                    false
                  end
        subscribed = plan["id"] == project.plan.id
        hash_array << plan.merge!({is_expired: expired , subscribed: subscribed})
      end
      hash_array
    end

Models:

class Plan < MysqlBase
  has_many :project_plans
end

class ProjectPlan < MysqlBase
  belongs_to :plan
  belongs_to :project
end

class Project < MysqlBase
  has_one :project_plan
  has_one :plan, :through => :project_plan
end

Schema:

create_table "plans", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.string "name"
    t.text "description"
    t.integer "email_limit"
    t.integer "validity"
    t.float "unit_price", limit: 24
    t.string "currency"
    t.integer "base_user_count"
    t.boolean "is_renewable"
    t.boolean "is_active"
    t.boolean "is_free"
    t.string "terms"
    t.integer "trial_days"
    t.boolean "default"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

create_table "project_plans", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.integer "plan_id"
    t.integer "project_id"
    t.datetime "start_date"
    t.datetime "end_date"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

Response

{
    "status": "OK",
    "status_code": 200,
    "message": "ok",
    "data": {
        "plans": [
            {
                "id": 1,
                "name": "Free",
                "description": "30 days free trial",
                "email_limit": 10000,
                "validity": 30,
                "unit_price": 0.0,
                "currency": "USD",
                "base_user_count": 0,
                "trial_days": 30,
                "is_free": true,
                "subscribed": false,
                "is_expired": false
            },
            {
                "id": 2,
                "name": "Standard",
                "description": "$3 for 100 users",
                "email_limit": 0,
                "validity": 30,
                "unit_price": 0.03,
                "currency": "USD",
                "base_user_count": 100,
                "trial_days": 0,
                "is_free": false,
                "subscribed": true,
                "is_expired": false
            }
        ]
    }
}

Each project has a separate project_plan. Plans are the same for all users. But what I have to do is to add two additional fields is_expired & subscribed with the API response

I am caching my response here using fetch. But is it the right way to doing cache as there are some DB & logical operations

If that isn't the right way what type of error can be happened here?

What will be the best approach to do this kind of caching?

CodePudding user response:

One solution here is to join the project plans table and select aggregates:

SELECT 
  plans.*,
  -- you will get integers since MySQL is a peasant database and does not have real boolean types
  COUNT(pp.id) > 0 AS subscribed,
  COALESCE(MAX(pp.ends_at) < NOW(), FALSE) AS is_expired
FROM plans
LEFT OUTER JOIN project_plans pp 
  ON pp.plan_id = plans.id
  AND pp.project_id = ?
GROUP BY plans.id
class Plan
  def with_statuses_for_project(project)
    pp = ProjectPlan.arel_table
    j = pp.join(arel_table).on(
      pp[:plan_id].eq(arel_table[:id])
                  .and(
                    pp[:project_id].eq(project.id)
                  )
    )
    .select(
      arel_table[Arel.star],
      "COUNT(pp.id) > 0 AS subscribed",
      "COALESCE(MAX(pp.ends_at) < NOW(), FALSE) AS is_expired"
    )
    .joins(j.join_sources)
    .group(:id)
  end
end
  • Related