8fa97b9513b66cccc2db021782863b3b

My Publication class has a few very similar virtual attributes. Is there any way to unite their setter and getter methods?

class Publication < ActiveRecord::Base

  def publisher_name
    self.publisher.name
  end
  
  def publisher_name=(name)
    self.publisher = Publisher.find_or_create_by_name(name.strip) unless name.blank?
  end
  
  def organization_name
    self.organization.name
  end

  def organization_name=(name)
      self.organization = Organization.find_or_create_by_name(name.strip) unless name.blank?
  end
end

Refactorings

No refactoring yet !

D41d8cd98f00b204e9800998ecf8427e

Spreelanka

November 20, 2008, November 20, 2008 15:12, permalink

No rating. Login to rate!

no doubt there is a better or faster way to do this.
there is certainly a shorter way to do this, but it's much slower.
I didn't write that version bc I can't remember the name of the method used to implement it. Something like Object#no_method.

class Publication < ActiveRecord::Base

  def publisher_name
    self.publisher.name
  end
  
  def publisher_name=(name)
    how_i_set_stuff!(Publisher,name)
  end
  
  def organization_name
    self.organization.name
  end

  def organization_name=(name)
    how_i_set_stuff!(Organization,name)
  end
protected
  def how_i_set_stuff!(obj,name)
    self.send(obj.to_s.downcase)=obj.find_or_create_by_name(name.strip) unless name.blank?
  end
end
60888d58de3bf08cbc0c73e67fd6fd86

Josh N

November 21, 2008, November 21, 2008 03:08, permalink

No rating. Login to rate!

This solution may be overkill if u only do this for one model. If you need it in many models in might be worth DRYing up the code like the way attr_accessor works

# a module that code gens the attributes
module ChildAccessor
  def attr_child_accessor(obj,attr_name)
    attr_name = attr_name.to_s
    class_name = obj.name.downcase
    method_name = "#{class_name}_#{attr_name.to_s}"

    class_eval <<-end_eval
      def #{method_name}
        self.#{class_name}.#{attr_name} 
      end

      def #{method_name}=(#{attr_name})
        unless #{attr_name}.blank?
          self.#{class_name} = 
            #{obj.name}.find_or_create_by_#{attr_name}(#{attr_name}.strip) 
        end
      end
    end_eval
  end 
end

# in config/environment.rb
ActiveRecord::Base.extend ChildAccessor 

class Publication < ActiveRecord::Base
  attr_child_accessor Publisher, :name
  attr_child_accessor Orginization, :name
end
60888d58de3bf08cbc0c73e67fd6fd86

Josh N

November 21, 2008, November 21, 2008 05:55, permalink

No rating. Login to rate!

removed string based class_eval in favor of define_method.. shorter code :)

module ChildAccessor
  def attr_child_accessor(obj,attr_name)
    child_name = obj.name.downcase
    method_name = "#{child_name}_#{attr_name.to_s}"

    class_eval do
      define_method method_name do
        self.send(child_name).send(attr_name)
      end

      define_method "#{method_name}=" do |name|
        self.send("#{child_name}=", obj.send("find_or_create_by_#{attr_name}", name.strip)) unless name.blank?
      end
    end
  end
end

ActiveRecord::Base.extend ChildAccessor

class Publication < ActiveRecord::Base
  attr_child_accessor Publisher, :name
  attr_child_accessor Orginization, :name
end
A8d3f35baafdaea851914b17dae9e1fc

Adam

November 21, 2008, November 21, 2008 06:12, permalink

No rating. Login to rate!
module NameAssociation
  def self.extended(object)
    object.instance_eval(<<-EOS, '(__NAME_ASSOCIATION__)', 1)
      def #{object.proxy_owner.to_s.downcase}_name=(name)
        find_or_create_by_name(name)
      end
    EOS
  end
end

class Publication < ActiveRecord::Base
  has_one :publication, :extend => NameAssociation
  has_one :organization, :extend => NameAssociation
  
  delegate :name, :name=, :to => :publication, :prefix => true
  delegate :name, :name=, :to => :organization, :prefix => true
end

Your refactoring





Format Copy from initial code

or Cancel