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, position: 0, **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
60
61
62
63
64
65
66
# File 'lib/declare_schema/model/field_spec.rb', line 36

def initialize(model, name, type, position: 0, **options)
  # TODO: TECH-5116
  # 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 = 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`?"
  when :bigint
    @type = :integer
    @options = options.merge(limit: 8)
  end

  unless type.in?([:text, :string])
    @options[:collation] and raise "collation may only given for :string and :text fields"
    @options[:charset]   and raise "charset may only given for :string and :text fields"
  end
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

#charsetObject



118
119
120
121
122
# File 'lib/declare_schema/model/field_spec.rb', line 118

def charset
  if ActiveRecord::Base.connection.class.name.match?(/mysql/i)
    (@options[:charset] || model.table_options[:charset] || Generators::DeclareSchema::Migration::Migrator.default_charset).to_s
  end
end

#collationObject



112
113
114
115
116
# File 'lib/declare_schema/model/field_spec.rb', line 112

def collation
  if ActiveRecord::Base.connection.class.name.match?(/mysql/i)
    (@options[:collation] || model.table_options[:collation] || Generators::DeclareSchema::Migration::Migrator.default_collation).to_s
  end
end

#defaultObject



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

def default
  @options[:default]
end

#different_to?(table_name, col_spec) ⇒ Boolean

Returns:



131
132
133
# File 'lib/declare_schema/model/field_spec.rb', line 131

def different_to?(table_name, col_spec)
  !same_as(table_name, col_spec)
end

#limitObject



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

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

#nullObject



104
105
106
# File 'lib/declare_schema/model/field_spec.rb', line 104

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

#precisionObject



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

def precision
  @options[:precision]
end

#same_as(table_name, col_spec) ⇒ Object



135
136
137
138
139
# File 'lib/declare_schema/model/field_spec.rb', line 135

def same_as(table_name, col_spec)
  same_type?(col_spec) &&
    same_attributes?(col_spec) &&
    (!type.in?([:text, :string]) || same_charset_and_collation?(table_name, col_spec))
end

#same_type?(col_spec) ⇒ Boolean

Returns:



124
125
126
127
128
129
# File 'lib/declare_schema/model/field_spec.rb', line 124

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



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

def scale
  @options[:scale]
end

#sql_optionsObject



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

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

#sql_typeObject



77
78
79
80
81
82
83
84
85
86
# File 'lib/declare_schema/model/field_spec.rb', line 77

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