require 'og/entity' # Add cloning functionality to entities. #-- # WARNING, gmosx: I haven't tested this extensively, # this is a donated patch. #++ module EntityMixin def og_clone(*args) Og::Entity.clone(self,*args) end end class Entity class << self # Entity copying support. Eventually this should all # be eval'd in at enchanting stage for the minor # speed increase. # TODO: Convert to enchantments on objects # Accepts source object, destination and ignore. # Source and destination are self explanatory; ignore # is a list of properties not to copy (i.e. # :create_time,:update_time). # By default sets the class variables directly on the # remote model instance, if you set use_setter_method to # true, uses create_time= style copying tactics, def copy_properties(source, destination, ignore = [], use_setter_method = false) property_copier(source, destination, ignore, use_setter_method, false) end # Copies relations of one record to another. Only copies # has_one, refers_to, belongs_to relationships as # has_many requires modifying of other objects and # cannot be copied (by design). If you think you need to copy # these relations, what you need is a joins_many relationship # which can be copied. def copy_inferior_relations(source, destination, ignore = []) real_ignore = Array.new # Map relation symbols to foreign keys. ignore.each do |symbol| source.class.relations.reject{|r| [Og::JoinsMany, Og::ManyToMany, Og::HasMany].include?(r.class)}.each do |relation| if relation.name == symbol.to_s real_ignore << relation.foreign_key.to_sym break end end end # Use instance variable property copier method. property_copier(source, destination, real_ignore, false, true) end def copy_equal_relations(source, destination, ignore = []) source.class.relations.reject{|r| not [Og::JoinsMany, Og::ManyToMany].include?(r.class)}.each do |relation| next if relation.name == nil or ignore.include?(relation.name) source.send(relation.name).each do |related| destination.send(relation.name).send(:<<, related) end end end # Copies all relations *except* HasMany which is impossible # to copy. Use a JoinsMany relation instead if you need a # copyable HasMany (which is irrational). def copy_relations(source, destination, ignore = []) copy_inferior_relations(source, destination, ignore) copy_equal_relations(source, destination, ignore) end # Clones an object in every possible way (cannot copy # HasMany but can copy all others - BelongsTo, etc). # Provide a source object as first arguments, the rest # (if any) are passed along to the initialize constructor # when calling new to make the copied object. def clone(source,*args) destination = source.class.new(*args) copy_properties(source, destination, [], false) # Must save here to copy join tables. destination.save! copy_relations(source, destination, []) destination.save! destination end # Does the work of clone_properties and copy_inferior_relations. # Syntax is the same with one extra field to tell the # routine what it is copying. def property_copier(source,destination,ignore,use_setter_method,relations) primary_key_symbol = source.class.primary_key.symbol source.class.properties.to_a.each do |symbol, property| next if primary_key_symbol == symbol or ignore.include?(symbol) or (relations and not property.relation) or (not relations and property.relation) variable = "@#{symbol}" if use_setter_method destination.send("#{symbol}=".to_sym,source.instance_variable_get(variable)) else destination.instance_variable_set(variable, source.instance_variable_get(variable)) end end end end end