Class: AddOns::Restaurant

Inherits:
ApplicationRecord show all
Includes:
IdentityCache
Defined in:
app/models/add_ons/restaurant.rb

Overview

Schema Information

Table name: restaurant_add_ons

id                       :bigint           not null, primary key
active                   :boolean          default(FALSE)
auto_extend              :boolean          default(FALSE)
end_date                 :date
is_visible_for_staff     :boolean          default(FALSE)
last_booking_was_made    :datetime
rank_in_restaurant_scope :integer          default(0)
start_date               :date
created_at               :datetime         not null
updated_at               :datetime         not null
add_on_id                :bigint
restaurant_id            :integer

Indexes

index_restaurant_add_ons_on_add_on_id      (add_on_id)
index_restaurant_add_ons_on_end_date       (end_date)
index_restaurant_add_ons_on_restaurant_id  (restaurant_id)
index_restaurant_add_ons_on_start_date     (start_date)
restaurant_add_on_start_end_date           (start_date,end_date)

Foreign Keys

fk_rails_...  (add_on_id => add_ons.id)
fk_rails_...  (restaurant_id => restaurants.id)

Constant Summary collapse

SHOW_TO_ALL_USERS_MODE =
'Show to all users'.freeze
PREVIEW_MODE =
'Preview Mode'.freeze
HIDDEN_MODE =
'Hidden'.freeze

Instance Method Summary collapse

Methods inherited from ApplicationRecord

sync_carrierwave_url

Instance Method Details

#active_and_not_expired?Boolean

Returns:

  • (Boolean)


95
96
97
# File 'app/models/add_ons/restaurant.rb', line 95

def active_and_not_expired?
  active? && end_date >= Time.thai_time.to_date
end

#available_at?(date, time, time_zone) ⇒ Boolean

Returns:

  • (Boolean)


230
231
232
233
234
235
# File 'app/models/add_ons/restaurant.rb', line 230

def available_at?(date, time, time_zone)
  requested_time = Time.use_zone(time_zone) { Time.zone.parse("#{date} #{time}") }
  fetch_addon.agendas.select do |agenda|
    agenda.available_at?(start_date, end_date, time_zone, requested_time)
  end.present?
end

#available_times(date, time_zone) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'app/models/add_ons/restaurant.rb', line 112

def available_times(date, time_zone)
  unless date.is_a?(Date)
    date = Time.use_zone(time_zone) { Time.zone.parse(date) }.to_date
  end

  start_time = date.to_time.beginning_of_day.to_i
  end_time = date.to_time.end_of_day.to_i

  available_times = []
  $inv_redis.with do |redis|
    cache_key = inventory_cache_key(time_zone)

    write_available_times(time_zone) if redis.zcard(cache_key).zero?

    # Retrieve indices of available times for the given date range
    indices = redis.zrangebyscore(cache_key, start_time, end_time)

    # Retrieve the actual available times from the indices using redis pipeline
    redis.pipelined do
      indices.each do |index|
        available_times.push(redis.zscore(cache_key, index))
      end
    end

    available_times.map { |time| Time.at(time.value.to_i).in_time_zone(time_zone).strftime('%H:%M') }
  end
end

#cleanup_stale_cache_keys(redis, current_key) ⇒ Integer

Cleans up stale cache keys for this restaurant add-on before writing new cache This prevents memory bloat from accumulating cache keys with different variable hashes

Parameters:

  • redis (Redis)

    Redis connection

  • current_key (String)

    The current cache key to preserve

Returns:

  • (Integer)

    Number of keys deleted



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'app/models/add_ons/restaurant.rb', line 180

def cleanup_stale_cache_keys(redis, current_key)
  deleted_count = 0
  cursor = '0'

  begin
    cursor, keys = redis.scan(cursor, match: inventory_cache_key_pattern, count: 100)

    keys_to_delete = keys.reject { |key| key == current_key }
    unless keys_to_delete.empty?
      # Use UNLINK for non-blocking delete; fallback to DEL if UNLINK unsupported
      begin
        deleted = redis.unlink(*keys_to_delete)
      rescue StandardError
        deleted = redis.del(*keys_to_delete)
      end
      deleted_count += deleted.to_i
    end
  end while cursor != '0'

  if deleted_count > 0
    HH_LOGGER.info('Cleaned up stale add-on inventory cache keys', {
                     restaurant_add_on_id: id,
                     restaurant_id: restaurant_id,
                     deleted_count: deleted_count,
                     current_key: current_key,
                   })
  end

  deleted_count
end

#determine_inventory_class(restaurant) ⇒ Object



103
104
105
106
107
108
109
110
# File 'app/models/add_ons/restaurant.rb', line 103

def determine_inventory_class(restaurant)
  is_dine_in = if restaurant.use_third_party_inventory?
                 nil
               else
                 restaurant.is_dine_in?
               end
  InventoryWrapper.new(restaurant_id: restaurant.id, is_dine_in: is_dine_in).inv_model
end

#determine_min_booking_timeObject



237
238
239
# File 'app/models/add_ons/restaurant.rb', line 237

def determine_min_booking_time
  fetch_addon.minimum_booking_time_in_advance.presence || 0
end

#fetch_addonObject

Instance Methods



78
79
80
81
82
83
84
85
86
# File 'app/models/add_ons/restaurant.rb', line 78

def fetch_addon
  @fetch_addon ||= begin
    Rails.cache.fetch "#{cache_key}/fetch_addon", expires_in: CACHEFLOW.generate_expiry do
      add_on
    end
  rescue TypeError
    add_on
  end
end

#inventory_cache_key(time_zone) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'app/models/add_ons/restaurant.rb', line 140

def inventory_cache_key(time_zone)
  data = fetch_addon.agendas.flat_map do |agenda|
    attributes = agenda.attributes.slice('start_time', 'end_time', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun',
                                         'exception_occurrences', 'single_occurrences')

    attributes['exception_occurrences'].map! do |exception_occurrence|
      if exception_occurrence.respond_to?(:[]) && exception_occurrence['start_date'].present?
        exception_occurrence['start_date'] = exception_occurrence['start_date'].to_date
      end
      if exception_occurrence.respond_to?(:[]) && exception_occurrence['end_date'].present?
        exception_occurrence['end_date'] = exception_occurrence['end_date'].to_date
      end
      exception_occurrence
    end

    attributes['single_occurrences'].map! do |single_occurrence|
      if single_occurrence['start_date'].present?
        single_occurrence['start_date'] = single_occurrence['start_date'].to_date
      end
      if single_occurrence['end_date'].present?
        single_occurrence['end_date'] = single_occurrence['end_date'].to_date
      end
      single_occurrence
    end
    attributes
  end
  "restaurant-add-on-inv:{restaurant_add_on_#{id}}:variables_#{CityHash.hash32([time_zone, data, end_date])}"
end

#inventory_cache_key_patternObject

Pattern for scanning all cache keys for this restaurant add-on Used for cleanup of stale cache keys when a new cache key is generated



171
172
173
# File 'app/models/add_ons/restaurant.rb', line 171

def inventory_cache_key_pattern
  "restaurant-add-on-inv:{restaurant_add_on_#{id}}:*"
end

#preview?Boolean

Returns:

  • (Boolean)


99
100
101
# File 'app/models/add_ons/restaurant.rb', line 99

def preview?
  is_visible_for_staff?
end

#statusObject



88
89
90
91
92
93
# File 'app/models/add_ons/restaurant.rb', line 88

def status
  return PREVIEW_MODE if active? && is_visible_for_staff?
  return SHOW_TO_ALL_USERS_MODE if active? && !is_visible_for_staff?

  HIDDEN_MODE
end

#write_available_times(time_zone) ⇒ Object



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'app/models/add_ons/restaurant.rb', line 211

def write_available_times(time_zone)
  data = fetch_addon.agendas.flat_map do |agenda|
    agenda.schedule_obj(start_date, end_date, time_zone)&.all_occurrences
  end

  $inv_redis.with do |redis|
    cache_key = inventory_cache_key(time_zone)

    redis.pipelined do
      # Assuming your data is in the format: "Wed, 15 Jan 2025 19:30:00 +07 +07:00"
      data.each_with_index do |available_time, index|
        redis.zadd(cache_key, available_time.to_i, index)
      end
      # Expire myzset after end_date + 1 day
      redis.expire(cache_key, (end_date + 1.day).to_time.to_i)
    end
  end
end