# -*- encoding: utf-8 module RailsERD # Describes an entity's attribute. Attributes correspond directly to # database columns. class Attribute TIMESTAMP_NAMES = %w{created_at created_on updated_at updated_on} #:nodoc: class << self def from_model(domain, model) #:nodoc: model.arel_table.columns.collect { |column| Attribute.new(domain, model, column) }.sort end end attr_reader :column #:nodoc: def initialize(domain, model, column) #:nodoc: @domain, @model, @column = domain, model, column end # The name of the attribute, equal to the column name. def name column.name end # The type of the attribute, equal to the Rails migration type. Can be any # of +:string+, +:integer+, +:boolean+, +:text+, etc. def type column.type end # Returns +true+ if this attribute is mandatory. Mandatory attributes # either have a presence validation (+validates_presence_of+), or have a # NOT NULL database constraint. def mandatory? !column.null or @model.validators_on(name).map(&:kind).include?(:presence) end # Returns +true+ if this attribute is the primary key of the entity. def primary_key? @model.arel_table.primary_key == name end # Returns +true+ if this attribute is used as a foreign key for any # relationship. def foreign_key? @domain.relationships_for(@model).map(&:associations).flatten.map(&:primary_key_name).include?(name) end # Returns +true+ if this attribute is one of the standard 'magic' Rails # timestamp columns, being +created_at+, +updated_at+, +created_on+ or # +updated_on+. def timestamp? TIMESTAMP_NAMES.include? name end def <=>(other) #:nodoc: name <=> other.name end def inspect #:nodoc: "#<#{self.class.name}:0x%.14x @column=#{name.inspect} @type=#{type.inspect}>" % (object_id << 1) end def to_s #:nodoc: name end # Returns a short description of the attribute type. If the attribute has # a non-standard limit or if it is mandatory, this information is included. # # Example output: # :integer:: int # :string, :limit => 255:: str # :string, :limit => 128:: str (128) # :boolean, :null => false:: bool * def type_description case type when :integer then "int" when :float then "float" when :decimal then "dec" when :datetime then "datetime" when :date then "date" when :timestamp then "timest" when :time then "time" when :text then "txt" when :string then "str" when :binary then "blob" when :boolean then "bool" else type.to_s end.tap do |desc| desc << " (#{column.limit})" if column.limit != @model.connection.native_database_types[type][:limit] desc << " ∗" if mandatory? # Add a hair space + low asterisk (Unicode characters). end end end end