module HoboFields module Model class FieldSpec class UnknownSqlTypeError < RuntimeError; end def initialize(model, name, type, options={}) raise ArgumentError, "you cannot provide a field spec for the primary key" if name == model.primary_key self.model = model self.name = name.to_sym self.type = type.is_a?(String) ? type.to_sym : type position = options.delete(:position) self.options = options self.position = position || model.field_specs.length end attr_accessor :model, :name, :type, :position, :options TYPE_SYNONYMS = [[:timestamp, :datetime]] begin MYSQL_COLUMN_CLASS = ActiveRecord::ConnectionAdapters::MysqlColumn rescue NameError MYSQL_COLUMN_CLASS = NilClass end begin SQLITE_COLUMN_CLASS = ActiveRecord::ConnectionAdapters::SQLiteColumn rescue NameError SQLITE_COLUMN_CLASS = NilClass end def sql_type options[:sql_type] or begin if native_type?(type) type else field_class = HoboFields.to_class(type) field_class && field_class::COLUMN_TYPE or raise UnknownSqlTypeError, "#{type.inspect} for #{model}.#{name}" end end end def limit options[:limit] || native_types[sql_type][:limit] end def precision options[:precision] end def scale options[:scale] end def null :null.in?(options) ? options[:null] : true end def default options[:default] end def comment options[:comment] end def same_type?(col_spec) t = sql_type TYPE_SYNONYMS.each do |synonyms| if t.in? synonyms return col_spec.type.in?(synonyms) end end t == col_spec.type end def different_to?(col_spec) !same_type?(col_spec) || # we should be able to use col_spec.comment, but col_spec has # a nil table_name for some strange reason. begin if model.table_exists? col_comment = ActiveRecord::Base.try.column_comment(col_spec.name, model.table_name) col_comment != nil && col_comment != comment else false end end || begin check_attributes = [:null, :default] check_attributes += [:precision, :scale] if sql_type == :decimal && !col_spec.is_a?(SQLITE_COLUMN_CLASS) # remove when rails fixes https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/2872 check_attributes -= [:default] if sql_type == :text && col_spec.is_a?(MYSQL_COLUMN_CLASS) check_attributes << :limit if sql_type.in?([:string, :text, :binary, :integer]) check_attributes.any? do |k| if k==:default && sql_type==:datetime col_spec.default.try.to_datetime != default.try.to_datetime else col_spec.send(k) != self.send(k) end end end end private def native_type?(type) type.in?(native_types.keys - [:primary_key]) end def native_types Generators::Hobo::Migration::Migrator.native_types end end end end