Class: DeclareSchema::Model::FieldSpec

Inherits:
Object
  • Object
show all
Defined in:
lib/declare_schema/model/field_spec.rb

Defined Under Namespace

Classes: UnknownSqlTypeError

Constant Summary collapse

MYSQL_TINYTEXT_LIMIT =
0xff
MYSQL_TEXT_LIMIT =
0xffff
MYSQL_MEDIUMTEXT_LIMIT =
0xff_ffff
MYSQL_LONGTEXT_LIMIT =
0xffff_ffff
MYSQL_TEXT_LIMITS_ASCENDING =
[MYSQL_TINYTEXT_LIMIT, MYSQL_TEXT_LIMIT, MYSQL_MEDIUMTEXT_LIMIT, MYSQL_LONGTEXT_LIMIT].freeze
TYPE_SYNONYMS =
{ timestamp: :datetime }.freeze
SQLITE_COLUMN_CLASS =
begin
  ActiveRecord::ConnectionAdapters::SQLiteColumn
rescue NameError
  NilClass
end

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model, name, type, options = {}) ⇒ FieldSpec

Returns a new instance of FieldSpec.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/declare_schema/model/field_spec.rb', line 36

def initialize(model, name, type, options = {})
  # Invoca change - searching for the primary key was causing an additional database read on every model load.  Assume
  # "id" which works for invoca.
  # raise ArgumentError, "you cannot provide a field spec for the primary key" if name == model.primary_key
  name == "id" and raise ArgumentError, "you cannot provide a field spec for the primary key"

  @model = model
  @name = name.to_sym
  type.is_a?(Symbol) or raise ArgumentError, "type must be a Symbol; got #{type.inspect}"
  @type = type
  position_option = options.delete(:position)
  @options = options

  case type
  when :text
    @options[:default] and raise "default may not be given for :text field #{model}##{@name}"
    if self.class.mysql_text_limits?
      @options[:limit] = self.class.round_up_mysql_text_limit(@options[:limit] || MYSQL_LONGTEXT_LIMIT)
    end
  when :string
    @options[:limit] or raise "limit must be given for :string field #{model}##{@name}: #{@options.inspect}; do you want `limit: 255`?"
  end
  @position = position_option || model.field_specs.length
end

Instance Attribute Details

#modelObject (readonly)

Returns the value of attribute model.



34
35
36
# File 'lib/declare_schema/model/field_spec.rb', line 34

def model
  @model
end

#nameObject (readonly)

Returns the value of attribute name.



34
35
36
# File 'lib/declare_schema/model/field_spec.rb', line 34

def name
  @name
end

#optionsObject (readonly)

Returns the value of attribute options.



34
35
36
# File 'lib/declare_schema/model/field_spec.rb', line 34

def options
  @options
end

#positionObject (readonly)

Returns the value of attribute position.



34
35
36
# File 'lib/declare_schema/model/field_spec.rb', line 34

def position
  @position
end

#typeObject (readonly)

Returns the value of attribute type.



34
35
36
# File 'lib/declare_schema/model/field_spec.rb', line 34

def type
  @type
end

Class Method Details

.mysql_text_limits?Boolean

method for easy stubbing in tests

Returns:



17
18
19
20
21
22
23
# File 'lib/declare_schema/model/field_spec.rb', line 17

def mysql_text_limits?
  if defined?(@mysql_text_limits)
    @mysql_text_limits
  else
    @mysql_text_limits = ActiveRecord::Base.connection.class.name.match?(/mysql/i)
  end
end

.round_up_mysql_text_limit(limit) ⇒ Object



25
26
27
28
29
30
31
# File 'lib/declare_schema/model/field_spec.rb', line 25

def round_up_mysql_text_limit(limit)
  MYSQL_TEXT_LIMITS_ASCENDING.find do |mysql_supported_text_limit|
    if limit <= mysql_supported_text_limit
      mysql_supported_text_limit
    end
  end or raise ArgumentError, "limit of #{limit} is too large for MySQL"
end

Instance Method Details

#defaultObject



101
102
103
# File 'lib/declare_schema/model/field_spec.rb', line 101

def default
  @options[:default]
end

#different_to?(col_spec) ⇒ Boolean

Returns:



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/declare_schema/model/field_spec.rb', line 112

def different_to?(col_spec)
  !same_type?(col_spec) ||
    begin
      native_type = native_types[type]
      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.class.name =~ /mysql/i
      check_attributes << :limit if sql_type.in?([:string, :binary, :varbinary, :integer, :enum]) ||
                                    (sql_type == :text && self.class.mysql_text_limits?)
      check_attributes.any? do |k|
        if k == :default
          case Rails::VERSION::MAJOR
          when 4
            col_spec.type_cast_from_database(col_spec.default) != col_spec.type_cast_from_database(default)
          else
            cast_type = ActiveRecord::Base.connection.lookup_cast_type_from_column(col_spec) or raise "cast_type not found for #{col_spec.inspect}"
            cast_type.deserialize(col_spec.default) != cast_type.deserialize(default)
          end
        else
          col_value = col_spec.send(k)
          if col_value.nil? && native_type
            col_value = native_type[k]
          end
          col_value != send(k)
        end
      end
    end
end

#limitObject



85
86
87
# File 'lib/declare_schema/model/field_spec.rb', line 85

def limit
  @options[:limit] || native_types[sql_type][:limit]
end

#nullObject



97
98
99
# File 'lib/declare_schema/model/field_spec.rb', line 97

def null
  !:null.in?(@options) || @options[:null]
end

#precisionObject



89
90
91
# File 'lib/declare_schema/model/field_spec.rb', line 89

def precision
  @options[:precision]
end

#same_type?(col_spec) ⇒ Boolean

Returns:



105
106
107
108
109
110
# File 'lib/declare_schema/model/field_spec.rb', line 105

def same_type?(col_spec)
  type = sql_type
  normalized_type           = TYPE_SYNONYMS[type] || type
  normalized_col_spec_type  = TYPE_SYNONYMS[col_spec.type] || col_spec.type
  normalized_type == normalized_col_spec_type
end

#scaleObject



93
94
95
# File 'lib/declare_schema/model/field_spec.rb', line 93

def scale
  @options[:scale]
end

#sql_optionsObject



81
82
83
# File 'lib/declare_schema/model/field_spec.rb', line 81

def sql_options
  @options.except(:ruby_default, :validates)
end

#sql_typeObject



70
71
72
73
74
75
76
77
78
79
# File 'lib/declare_schema/model/field_spec.rb', line 70

def sql_type
  @options[:sql_type] || begin
                          if native_type?(type)
                            type
                          else
                            field_class = DeclareSchema.to_class(type)
                            field_class && field_class::COLUMN_TYPE or raise UnknownSqlTypeError, "#{type.inspect} for #{model}##{@name}"
                          end
                        end
end