Improved cache backend. Heavy reduce of sql queries.
This commit is contained in:
parent
751ea0b98b
commit
be31823d89
25 changed files with 244 additions and 146 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -13,7 +13,7 @@
|
||||||
# Ignore all logfiles and tempfiles.
|
# Ignore all logfiles and tempfiles.
|
||||||
/log/*.log
|
/log/*.log
|
||||||
/tmp/websocket/*
|
/tmp/websocket/*
|
||||||
/tmp/cache/*
|
/tmp/cache*
|
||||||
/tmp/pids/*
|
/tmp/pids/*
|
||||||
/public/assets/*.*
|
/public/assets/*.*
|
||||||
/public/assets/app/*
|
/public/assets/app/*
|
||||||
|
|
|
@ -445,6 +445,68 @@ returns
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
|
activate latest change on create, update, touch and destroy
|
||||||
|
|
||||||
|
class Model < ApplicationModel
|
||||||
|
latest_change_support
|
||||||
|
end
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.latest_change_support
|
||||||
|
after_create :latest_change_set_from_observer
|
||||||
|
after_update :latest_change_set_from_observer
|
||||||
|
after_touch :latest_change_set_from_observer
|
||||||
|
after_destroy :latest_change_set_from_observer_destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
def latest_change_set_from_observer
|
||||||
|
self.class.latest_change_set(self.updated_at)
|
||||||
|
end
|
||||||
|
def latest_change_set_from_observer_destroy
|
||||||
|
self.class.latest_change_set(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.latest_change_set(updated_at)
|
||||||
|
key = "#{self.new.class.name}_latest_change"
|
||||||
|
expires_in = 31_536_000 # 1 year
|
||||||
|
|
||||||
|
if updated_at == nil
|
||||||
|
Cache.delete( key )
|
||||||
|
else
|
||||||
|
Cache.write( key, updated_at, { :expires_in => expires_in } )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
get latest updated_at object timestamp
|
||||||
|
|
||||||
|
latest_change = Ticket.latest_change
|
||||||
|
|
||||||
|
returns
|
||||||
|
|
||||||
|
result = timestamp
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.latest_change
|
||||||
|
key = "#{self.new.class.name}_latest_change"
|
||||||
|
updated_at = Cache.get( key )
|
||||||
|
|
||||||
|
# if we do not have it cached, do lookup
|
||||||
|
if !updated_at
|
||||||
|
o = self.select(:updated_at).order(updated_at: :desc).limit(1).first
|
||||||
|
if o
|
||||||
|
updated_at = o.updated_at
|
||||||
|
self.latest_change_set(updated_at)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
updated_at
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
activate client notify support on create, update, touch and destroy
|
activate client notify support on create, update, touch and destroy
|
||||||
|
|
||||||
class Model < ApplicationModel
|
class Model < ApplicationModel
|
||||||
|
|
|
@ -4,4 +4,7 @@ class EmailAddress < ApplicationModel
|
||||||
has_many :groups, :after_add => :cache_update, :after_remove => :cache_update
|
has_many :groups, :after_add => :cache_update, :after_remove => :cache_update
|
||||||
validates :realname, :presence => true
|
validates :realname, :presence => true
|
||||||
validates :email, :presence => true
|
validates :email, :presence => true
|
||||||
|
|
||||||
|
latest_change_support
|
||||||
|
|
||||||
end
|
end
|
|
@ -8,4 +8,5 @@ class Group < ApplicationModel
|
||||||
|
|
||||||
activity_stream_support :role => Z_ROLENAME_ADMIN
|
activity_stream_support :role => Z_ROLENAME_ADMIN
|
||||||
history_support
|
history_support
|
||||||
|
latest_change_support
|
||||||
end
|
end
|
|
@ -15,5 +15,5 @@ class Organization < ApplicationModel
|
||||||
history_support
|
history_support
|
||||||
search_index_support
|
search_index_support
|
||||||
notify_clients_support
|
notify_clients_support
|
||||||
|
latest_change_support
|
||||||
end
|
end
|
|
@ -5,4 +5,5 @@ class Role < ApplicationModel
|
||||||
has_and_belongs_to_many :users, :after_add => :cache_update, :after_remove => :cache_update
|
has_and_belongs_to_many :users, :after_add => :cache_update, :after_remove => :cache_update
|
||||||
validates :name, :presence => true
|
validates :name, :presence => true
|
||||||
activity_stream_support :role => Z_ROLENAME_ADMIN
|
activity_stream_support :role => Z_ROLENAME_ADMIN
|
||||||
|
latest_change_support
|
||||||
end
|
end
|
|
@ -3,4 +3,5 @@
|
||||||
class Signature < ApplicationModel
|
class Signature < ApplicationModel
|
||||||
has_many :groups, :after_add => :cache_update, :after_remove => :cache_update
|
has_many :groups, :after_add => :cache_update, :after_remove => :cache_update
|
||||||
validates :name, :presence => true
|
validates :name, :presence => true
|
||||||
|
latest_change_support
|
||||||
end
|
end
|
|
@ -20,6 +20,8 @@ class Ticket < ApplicationModel
|
||||||
|
|
||||||
notify_clients_support
|
notify_clients_support
|
||||||
|
|
||||||
|
latest_change_support
|
||||||
|
|
||||||
activity_stream_support :ignore_attributes => {
|
activity_stream_support :ignore_attributes => {
|
||||||
:create_article_type_id => true,
|
:create_article_type_id => true,
|
||||||
:create_article_sender_id => true,
|
:create_article_sender_id => true,
|
||||||
|
|
|
@ -40,9 +40,11 @@ class Ticket::Article < ApplicationModel
|
||||||
|
|
||||||
class Sender < ApplicationModel
|
class Sender < ApplicationModel
|
||||||
validates :name, :presence => true
|
validates :name, :presence => true
|
||||||
|
latest_change_support
|
||||||
end
|
end
|
||||||
|
|
||||||
class Type < ApplicationModel
|
class Type < ApplicationModel
|
||||||
validates :name, :presence => true
|
validates :name, :presence => true
|
||||||
|
latest_change_support
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -4,6 +4,8 @@ class Ticket::State < ApplicationModel
|
||||||
belongs_to :state_type, :class_name => 'Ticket::StateType'
|
belongs_to :state_type, :class_name => 'Ticket::StateType'
|
||||||
validates :name, :presence => true
|
validates :name, :presence => true
|
||||||
|
|
||||||
|
latest_change_support
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
list tickets by customer
|
list tickets by customer
|
||||||
|
|
|
@ -3,4 +3,5 @@
|
||||||
class Ticket::StateType < ApplicationModel
|
class Ticket::StateType < ApplicationModel
|
||||||
has_many :states, :class_name => 'Ticket::State'
|
has_many :states, :class_name => 'Ticket::State'
|
||||||
validates :name, :presence => true
|
validates :name, :presence => true
|
||||||
|
latest_change_support
|
||||||
end
|
end
|
|
@ -66,9 +66,6 @@ module Zammad
|
||||||
# Version of your assets, change this if you want to expire all your assets
|
# Version of your assets, change this if you want to expire all your assets
|
||||||
config.assets.version = '1.0'
|
config.assets.version = '1.0'
|
||||||
|
|
||||||
# Use a different cache store in production
|
|
||||||
config.cache_store = :file_store, 'tmp/cache/file_store'
|
|
||||||
|
|
||||||
# REST api path
|
# REST api path
|
||||||
config.api_path = '/api/v1'
|
config.api_path = '/api/v1'
|
||||||
|
|
||||||
|
|
|
@ -37,4 +37,8 @@ Zammad::Application.configure do
|
||||||
:live_reload_port => 35738,
|
:live_reload_port => 35738,
|
||||||
:source => :vendored
|
:source => :vendored
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# define cache store
|
||||||
|
config.cache_store = :file_store, 'tmp/cache_file_store_development'
|
||||||
|
|
||||||
end
|
end
|
|
@ -77,4 +77,8 @@ Zammad::Application.configure do
|
||||||
|
|
||||||
# Use default logging formatter so that PID and timestamp are not suppressed
|
# Use default logging formatter so that PID and timestamp are not suppressed
|
||||||
config.log_formatter = ::Logger::Formatter.new
|
config.log_formatter = ::Logger::Formatter.new
|
||||||
|
|
||||||
|
# define cache store
|
||||||
|
config.cache_store = :file_store, 'tmp/cache_file_store_production'
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -46,4 +46,7 @@ Zammad::Application.configure do
|
||||||
# Enable autoload
|
# Enable autoload
|
||||||
config.dependency_loading = true
|
config.dependency_loading = true
|
||||||
|
|
||||||
|
# define cache store
|
||||||
|
config.cache_store = :file_store, 'tmp/cache_file_store_test'
|
||||||
|
|
||||||
end
|
end
|
42
lib/cache.rb
42
lib/cache.rb
|
@ -1,8 +1,32 @@
|
||||||
module Cache
|
module Cache
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
delete a cache
|
||||||
|
|
||||||
|
Cache.delete( 'some_key' )
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
def self.delete( key )
|
def self.delete( key )
|
||||||
# puts 'Cache.delete' + key.to_s
|
# puts 'Cache.delete' + key.to_s
|
||||||
Rails.cache.delete( key.to_s )
|
Rails.cache.delete( key.to_s )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
write a cache
|
||||||
|
|
||||||
|
Cache.write(
|
||||||
|
'some_key',
|
||||||
|
{ :some => { :data => { 'structure' } } },
|
||||||
|
{
|
||||||
|
:expires_in => 24.hours, # optional
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
def self.write( key, data, params = {} )
|
def self.write( key, data, params = {} )
|
||||||
if !params[:expires_in]
|
if !params[:expires_in]
|
||||||
params[:expires_in] = 24.hours
|
params[:expires_in] = 24.hours
|
||||||
|
@ -14,10 +38,28 @@ module Cache
|
||||||
puts "NOTICE: #{e.message}"
|
puts "NOTICE: #{e.message}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
get a cache
|
||||||
|
|
||||||
|
value = Cache.write( 'some_key' )
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
def self.get( key )
|
def self.get( key )
|
||||||
# puts 'Cache.get: ' + key.to_s
|
# puts 'Cache.get: ' + key.to_s
|
||||||
Rails.cache.read( key.to_s )
|
Rails.cache.read( key.to_s )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
clear whole cache store
|
||||||
|
|
||||||
|
Cache.clear
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
def self.clear
|
def self.clear
|
||||||
# puts 'Cache.clear...'
|
# puts 'Cache.clear...'
|
||||||
# workaround, set test cache before clear whole cache, Rails.cache.clear complains about not existing cache dir
|
# workaround, set test cache before clear whole cache, Rails.cache.clear complains about not existing cache dir
|
||||||
|
|
|
@ -8,50 +8,10 @@ class Sessions::Backend::Collections::Base
|
||||||
@last_change = nil
|
@last_change = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def collection_key
|
|
||||||
"collections::load::#{ self.class.to_s }::#{ @user.id }"
|
|
||||||
end
|
|
||||||
|
|
||||||
def load
|
def load
|
||||||
#puts "-LOAD--------#{self.collection_key}"
|
|
||||||
# check timeout
|
|
||||||
cache = Sessions::CacheIn.get( self.collection_key )
|
|
||||||
return cache if @last_change && cache
|
|
||||||
#puts "---REAL FETCH #{@user.id}"
|
|
||||||
# update last changed
|
|
||||||
last = self.class.model.constantize.select('updated_at').order('updated_at DESC').first
|
|
||||||
if last
|
|
||||||
@last_change = last.updated_at
|
|
||||||
end
|
|
||||||
|
|
||||||
# if no entry exists, remember last check
|
|
||||||
if !@last_change
|
|
||||||
@last_change = Time.now
|
|
||||||
end
|
|
||||||
|
|
||||||
# get whole collection
|
# get whole collection
|
||||||
all = self.class.model.constantize.all
|
self.class.model.constantize.all
|
||||||
|
|
||||||
# set new timeout
|
|
||||||
Sessions::CacheIn.set( self.collection_key, all, { :expires_in => 10.minutes } )
|
|
||||||
|
|
||||||
all
|
|
||||||
end
|
|
||||||
|
|
||||||
def changed?
|
|
||||||
# if no data has been delivered till now
|
|
||||||
return true if !@last_change
|
|
||||||
|
|
||||||
# check if update has been done
|
|
||||||
last = self.class.model.constantize.select('updated_at').order('updated_at DESC').first
|
|
||||||
return false if !last
|
|
||||||
return false if last.updated_at == @last_change
|
|
||||||
|
|
||||||
# delete collection cache
|
|
||||||
Sessions::CacheIn.delete( self.collection_key )
|
|
||||||
|
|
||||||
# collection has changed
|
|
||||||
true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def client_key
|
def client_key
|
||||||
|
@ -83,7 +43,12 @@ class Sessions::Backend::Collections::Base
|
||||||
# set new timeout
|
# set new timeout
|
||||||
Sessions::CacheIn.set( self.client_key, true, { :expires_in => 10.seconds } )
|
Sessions::CacheIn.set( self.client_key, true, { :expires_in => 10.seconds } )
|
||||||
|
|
||||||
return if !self.changed?
|
# check if update has been done
|
||||||
|
last_change = self.class.model.constantize.latest_change
|
||||||
|
return if last_change == @last_change
|
||||||
|
@last_change = last_change
|
||||||
|
|
||||||
|
# load current data
|
||||||
items = self.load
|
items = self.load
|
||||||
|
|
||||||
return if !items||items.empty?
|
return if !items||items.empty?
|
||||||
|
|
|
@ -3,28 +3,6 @@ class Sessions::Backend::Collections::Organization < Sessions::Backend::Collecti
|
||||||
|
|
||||||
def load
|
def load
|
||||||
|
|
||||||
# check timeout
|
|
||||||
cache = Sessions::CacheIn.get( self.collection_key )
|
|
||||||
return cache if @last_change && cache
|
|
||||||
|
|
||||||
# update last changed
|
|
||||||
if !@user.is_role('Customer')
|
|
||||||
last = self.class.model.constantize.select('updated_at').order('updated_at DESC').first
|
|
||||||
if last
|
|
||||||
@last_change = last.updated_at
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if @user.organization_id
|
|
||||||
last = Organization.where( :id => @user.organization_id ).first
|
|
||||||
@last_change = last.updated_at
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# if no entry exists, remember last check
|
|
||||||
if !@last_change
|
|
||||||
@last_change = Time.now
|
|
||||||
end
|
|
||||||
|
|
||||||
# get whole collection
|
# get whole collection
|
||||||
all = []
|
all = []
|
||||||
if !@user.is_role('Customer')
|
if !@user.is_role('Customer')
|
||||||
|
@ -35,33 +13,7 @@ class Sessions::Backend::Collections::Organization < Sessions::Backend::Collecti
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# set new timeout
|
|
||||||
Sessions::CacheIn.set( self.collection_key, all, { :expires_in => 10.minutes } )
|
|
||||||
|
|
||||||
all
|
all
|
||||||
end
|
end
|
||||||
|
|
||||||
def changed?
|
|
||||||
|
|
||||||
# if no data has been delivered till now
|
|
||||||
return true if !@last_change
|
|
||||||
|
|
||||||
# check if update has been done
|
|
||||||
if !@user.is_role('Customer')
|
|
||||||
last = self.class.model.constantize.select('updated_at').order('updated_at DESC').first
|
|
||||||
else
|
|
||||||
if @user.organization_id
|
|
||||||
last = Organization.where( :id => @user.organization_id ).first
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false if !last
|
|
||||||
return false if last.updated_at == @last_change
|
|
||||||
|
|
||||||
# delete collection cache
|
|
||||||
Sessions::CacheIn.delete( self.collection_key )
|
|
||||||
|
|
||||||
# collection has changed
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
|
@ -36,7 +36,7 @@ class Sessions::Backend::TicketCreate
|
||||||
return if timeout
|
return if timeout
|
||||||
|
|
||||||
# set new timeout
|
# set new timeout
|
||||||
Sessions::CacheIn.set( self.client_key, true, { :expires_in => 30.seconds } )
|
Sessions::CacheIn.set( self.client_key, true, { :expires_in => 60.seconds } )
|
||||||
|
|
||||||
ticket_create_attributes = self.load
|
ticket_create_attributes = self.load
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ class Sessions::Backend::TicketOverviewIndex
|
||||||
@client = client
|
@client = client
|
||||||
@client_id = client_id
|
@client_id = client_id
|
||||||
@last_change = nil
|
@last_change = nil
|
||||||
|
@last_ticket_change = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def load
|
def load
|
||||||
|
@ -31,13 +32,19 @@ class Sessions::Backend::TicketOverviewIndex
|
||||||
|
|
||||||
def push
|
def push
|
||||||
|
|
||||||
# check timeout
|
# check check interval
|
||||||
timeout = Sessions::CacheIn.get( self.client_key )
|
timeout = Sessions::CacheIn.get( self.client_key )
|
||||||
return if timeout
|
return if timeout
|
||||||
|
|
||||||
# set new timeout
|
# reset check interval
|
||||||
Sessions::CacheIn.set( self.client_key, true, { :expires_in => 20.seconds } )
|
Sessions::CacheIn.set( self.client_key, true, { :expires_in => 5.seconds } )
|
||||||
|
|
||||||
|
# check if min one ticket has changed
|
||||||
|
last_ticket_change = Ticket.latest_change
|
||||||
|
return if last_ticket_change == @last_ticket_change
|
||||||
|
@last_ticket_change = last_ticket_change
|
||||||
|
|
||||||
|
# load current data
|
||||||
data = self.load
|
data = self.load
|
||||||
|
|
||||||
return if !data
|
return if !data
|
||||||
|
|
|
@ -4,6 +4,7 @@ class Sessions::Backend::TicketOverviewList
|
||||||
@client = client
|
@client = client
|
||||||
@client_id = client_id
|
@client_id = client_id
|
||||||
@last_change = nil
|
@last_change = nil
|
||||||
|
@last_ticket_change = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def load
|
def load
|
||||||
|
@ -41,13 +42,19 @@ class Sessions::Backend::TicketOverviewList
|
||||||
|
|
||||||
def push
|
def push
|
||||||
|
|
||||||
# check timeout
|
# check interval
|
||||||
timeout = Sessions::CacheIn.get( self.client_key )
|
timeout = Sessions::CacheIn.get( self.client_key )
|
||||||
return if timeout
|
return if timeout
|
||||||
|
|
||||||
# set new timeout
|
# reset check interval
|
||||||
Sessions::CacheIn.set( self.client_key, true, { :expires_in => 20.seconds } )
|
Sessions::CacheIn.set( self.client_key, true, { :expires_in => 6.seconds } )
|
||||||
|
|
||||||
|
# check if min one ticket has changed
|
||||||
|
last_ticket_change = Ticket.latest_change
|
||||||
|
return if last_ticket_change == @last_ticket_change
|
||||||
|
@last_ticket_change = last_ticket_change
|
||||||
|
|
||||||
|
# load current data
|
||||||
items = self.load
|
items = self.load
|
||||||
|
|
||||||
return if !items
|
return if !items
|
||||||
|
|
|
@ -45,14 +45,6 @@ module Sessions::CacheIn
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.get_time( key, params = {} )
|
|
||||||
data = self.get( key, params )
|
|
||||||
if data
|
|
||||||
return @@data_time[key]
|
|
||||||
end
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.get( key, params = {} )
|
def self.get( key, params = {} )
|
||||||
# puts 'CacheIn.get:' + key + '-' + @@data[ key ].inspect
|
# puts 'CacheIn.get:' + key + '-' + @@data[ key ].inspect
|
||||||
return if self.expired( key, params )
|
return if self.expired( key, params )
|
||||||
|
|
|
@ -110,4 +110,12 @@ class CacheTest < ActiveSupport::TestCase
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# verify if second cache write overwrite first one
|
||||||
|
test 'cache reset' do
|
||||||
|
Cache.write( 'some_reset_key', 123 )
|
||||||
|
Cache.write( 'some_reset_key', 12356 )
|
||||||
|
cache = Cache.get( 'some_reset_key' )
|
||||||
|
assert_equal( cache, 12356, 'verify' )
|
||||||
|
end
|
||||||
end
|
end
|
|
@ -123,4 +123,46 @@ class TicketTest < ActiveSupport::TestCase
|
||||||
delete = ticket.destroy
|
delete = ticket.destroy
|
||||||
assert( delete, "ticket destroy" )
|
assert( delete, "ticket destroy" )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
test 'ticket latest change' do
|
||||||
|
ticket1 = Ticket.create(
|
||||||
|
:title => 'latest change 1',
|
||||||
|
:group => Group.lookup( :name => 'Users'),
|
||||||
|
:customer_id => 2,
|
||||||
|
:state => Ticket::State.lookup( :name => 'new' ),
|
||||||
|
:priority => Ticket::Priority.lookup( :name => '2 normal' ),
|
||||||
|
:updated_by_id => 1,
|
||||||
|
:created_by_id => 1,
|
||||||
|
)
|
||||||
|
assert_equal( Ticket.latest_change.to_s, ticket1.updated_at.to_s )
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
ticket2 = Ticket.create(
|
||||||
|
:title => 'latest change 2',
|
||||||
|
:group => Group.lookup( :name => 'Users'),
|
||||||
|
:customer_id => 2,
|
||||||
|
:state => Ticket::State.lookup( :name => 'new' ),
|
||||||
|
:priority => Ticket::Priority.lookup( :name => '2 normal' ),
|
||||||
|
:updated_by_id => 1,
|
||||||
|
:created_by_id => 1,
|
||||||
|
)
|
||||||
|
assert_equal( Ticket.latest_change.to_s, ticket2.updated_at.to_s )
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
ticket1.title = 'latest change 1 - 1'
|
||||||
|
ticket1.save
|
||||||
|
assert_equal( Ticket.latest_change.to_s, ticket1.updated_at.to_s )
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
ticket1.touch
|
||||||
|
assert_equal( Ticket.latest_change.to_s, ticket1.updated_at.to_s )
|
||||||
|
|
||||||
|
ticket1.destroy
|
||||||
|
assert_equal( Ticket.latest_change.to_s, ticket2.updated_at.to_s )
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
Loading…
Reference in a new issue