Module: Cms::Behaviors::Versioning::InstanceMethods

Defined in:
lib/cms/behaviors/versioning.rb

Instance Method Summary collapse

Instance Method Details

#as_of_draft_versionObject



290
291
292
# File 'lib/cms/behaviors/versioning.rb', line 290

def as_of_draft_version
  draft.build_object_from_version
end

#as_of_version(version) ⇒ ContentBlock

Find a Content Block as of a specific version.

Parameters:

  • version (Integer)

    The specific version of the block to look up

Returns:

  • (ContentBlock)

    The block as of the state it existed at ‘version’.

Raises:

  • (ActiveRecord::RecordNotFound)


298
299
300
301
302
# File 'lib/cms/behaviors/versioning.rb', line 298

def as_of_version(version)
  v = find_version(version)
  raise ActiveRecord::RecordNotFound.new("version #{version.inspect} does not exist for <#{self.class}:#{id}>") unless v
  v.build_object_from_version
end

#build_new_versionObject

Build a new version of this record and associate it with this record.

Called as a before_create in order to correctly allow any other associations to be saved correctly. Called explicitly during update, where it will just define the new_version to be saved.



251
252
253
254
# File 'lib/cms/behaviors/versioning.rb', line 251

def build_new_version
  @new_version = build_new_version_and_add_to_versions_list_for_saving
  logger.debug { "New version of #{self.class}::Version is #{@new_version.attributes}" }
end

#build_new_version_and_add_to_versions_list_for_savingObject



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/cms/behaviors/versioning.rb', line 160

def build_new_version_and_add_to_versions_list_for_saving
  # First get the values from the draft
  attrs = draft_attributes

  # Now overwrite all values
  (self.class.versioned_columns - %w(  version  )).each do |col|
    attrs[col] = send(col)
  end

  attrs[:version_comment] = @version_comment || default_version_comment
  @version_comment = nil
  #puts "Im a '#{self.class}', vc = #{self.class.version_class}"
  new_version = versions.build(attrs)
  new_version.version = new_record? ? 1 : (draft.version.to_i + 1)
  after_build_new_version(new_version) if respond_to?(:after_build_new_version)
  new_version
end

#create_or_updateObject

ActiveRecord 3.0.0 call chain ActiveRecord 3 now uses basic inheritence rather than alias_method_chain. The order in which ActiveRecord::Base includes methods (at the bottom of activerecord) repeatedly overrides save/save! with chains of ‘super’

Callstack order as observed

  1. ActiveRecord::Base#save - The original method called by client

AR::Transactions#save
AR::Dirty#save
AR::Validations#save
ActiveRecord::Persistence#save
ActiveRecord::Persistence#create_or_update
AR::Callbacks#create_or_update (runs :save callbacks)

This aliases the original ActiveRecord::Base.save method, in order to change how calling save works. It should do the following things:

  1. If the record is unchanged, no save is performed, but true is returned. (Skipping after_save callbacks)

  2. If its an update, a new version is created and that is saved.

  3. If new record, its version is set to 1, and its published if needed.



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/cms/behaviors/versioning.rb', line 216

def create_or_update
  logger.debug { "#{self.class}#create_or_update called. Published = #{!!publish_on_save}" }
  self.skip_callbacks = false
  unless different_from_last_draft?
    logger.debug { "No difference between this version and last. Skipping save" }
    if !published && publish_on_save
      logger.debug { "Publishing current draft version" }
      return publish
    else
      self.skip_callbacks = true
      return true
    end
  end
  logger.debug { "Saving #{self.class} #{self.attributes}" }
  if new_record?
    self.version = 1
    # This should call ActiveRecord::Callbacks#create_or_update, which will correctly trigger the :save callback_chain
    saved_correctly = super

    compatible_clear_changes_information
  else
    logger.debug { "#{self.class}#update" }
    # Because we are 'skipping' the normal ActiveRecord update here, we must manually call the save callback chain.
    run_callbacks :save do
      saved_correctly = @new_version.save
    end
  end
  publish_if_needed
  return saved_correctly
end

#current_versionObject



282
283
284
# File 'lib/cms/behaviors/versioning.rb', line 282

def current_version
  find_version(self.version)
end

#default_version_commentObject



185
186
187
188
189
190
191
# File 'lib/cms/behaviors/versioning.rb', line 185

def default_version_comment
  if new_record?
    "Created"
  else
    "Changed #{(changes.keys - %w[  version created_by_id updated_by_id  ]).sort.join(', ')}"
  end
end

#different_from_last_draft?Boolean

Returns:

  • (Boolean)


346
347
348
349
350
351
352
353
354
# File 'lib/cms/behaviors/versioning.rb', line 346

def different_from_last_draft?
  return true if self.changed?
  last_draft = self.draft
  return true unless last_draft
  (self.class.versioned_columns - %w(  version  )).each do |col|
    return true if self.send(col) != last_draft.send(col)
  end
  false
end

#draft<Class>::Version

Returns the most recently created Version for this class. Drafts are the most recent change from the _versions table for a given content item.

i.e. For Cms::Page, this would return Cms::Page::Version

Returns:

  • (<Class>::Version)

    The version for this class that represents the draft.



265
266
267
# File 'lib/cms/behaviors/versioning.rb', line 265

def draft
  versions.order("version desc").first
end

#draft_attributesObject



178
179
180
181
182
183
# File 'lib/cms/behaviors/versioning.rb', line 178

def draft_attributes
  # When there is no draft, we'll just copy the attributes from this object
  # Otherwise we need to use the draft
  d = new_record? ? self : draft
  self.class.versioned_columns.inject({}) { |attrs, col| attrs[col] = d.send(col); attrs }
end

#draft_version?Boolean

Returns:

  • (Boolean)


269
270
271
272
# File 'lib/cms/behaviors/versioning.rb', line 269

def draft_version?
  return true unless draft
  version == draft.version
end

#find_version(number) ⇒ Object



286
287
288
# File 'lib/cms/behaviors/versioning.rb', line 286

def find_version(number)
  versions.where(:version => number).first
end

#initialize_versionObject



146
147
148
# File 'lib/cms/behaviors/versioning.rb', line 146

def initialize_version
  self.version = 1 if new_record?
end

#live_versionObject



274
275
276
# File 'lib/cms/behaviors/versioning.rb', line 274

def live_version
  find_version(self.class.find(id).version)
end

#live_version?Boolean

Returns:

  • (Boolean)


278
279
280
# File 'lib/cms/behaviors/versioning.rb', line 278

def live_version?
  version == self.class.find(id).version
end

#revertObject



304
305
306
307
# File 'lib/cms/behaviors/versioning.rb', line 304

def revert
  draft_version = draft.version
  revert_to(draft_version - 1) unless draft_version == 1
end

#revert_to(version, options = {}) ⇒ Object

Parameters:

  • version (Integer)

    To revert to

  • options (Hash) (defaults to: {})

    Values to set prior to saving the updated record.



332
333
334
335
# File 'lib/cms/behaviors/versioning.rb', line 332

def revert_to(version, options={})
  revert_to_without_save(version, options)
  save
end

#revert_to_without_save(version, options) ⇒ Object



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/cms/behaviors/versioning.rb', line 309

def revert_to_without_save(version, options)
  raise "Version parameter missing" if version.blank?
  revert_to_version = find_version(version)
  raise "Could not find version #{version}" unless revert_to_version
  self.before_revert(revert_to_version) if self.respond_to?(:before_revert)

  (self.class.versioned_columns - ["version"]).each do |a|
    send("#{a}=", revert_to_version.send(a))
  end


  options.keys.each do |key|
    send("#{key}=", options[key])
  end

  self.after_revert(revert_to_version) if self.respond_to?(:after_revert)
  self.version_comment = "Reverted to version #{version}"
  self.publish_on_save = false
  self
end

#save!(perform_validations = true) ⇒ Object



256
257
258
# File 'lib/cms/behaviors/versioning.rb', line 256

def save!(perform_validations=true)
  save(:validate => perform_validations) || raise(ActiveRecord::RecordNotSaved.new(errors.full_messages))
end

#update_latest_versionObject

Used in migrations and as a callback.



151
152
153
154
155
156
157
158
# File 'lib/cms/behaviors/versioning.rb', line 151

def update_latest_version
  #Rails 3 could use update_column here instead
  if respond_to? :latest_version
    sql = "UPDATE #{self.class.table_name} SET latest_version = #{draft.version} where id = #{self.id}"
    self.class.connection.execute sql
    self.latest_version = draft.version # So we don't need to #reload this object. Probably marks it as dirty though, which could have weird side effects.
  end
end

#version_commentObject



337
338
339
# File 'lib/cms/behaviors/versioning.rb', line 337

def version_comment
  @version_comment
end

#version_comment=(version_comment) ⇒ Object



341
342
343
344
# File 'lib/cms/behaviors/versioning.rb', line 341

def version_comment=(version_comment)
  @version_comment = version_comment
  send(:changed_attributes)["version_comment"] = @version_comment
end