Module: DeclareSchema::Model::ClassMethods
- Defined in:
- lib/declare_schema/model.rb
Instance Method Summary collapse
-
#_declared_primary_key ⇒ Object
returns the primary key (String) as declared with primary_key = unlike the ‘primary_key` method, DOES NOT query the database to find the actual primary key in use right now if no explicit primary key set, returns the _default_declared_primary_key.
- #_infer_fk_limit(foreign_key_column, reflection) ⇒ Object
-
#attr_type(name) ⇒ Object
Returns the type (a class) for a given field or association.
-
#belongs_to(name, scope = nil, **options) ⇒ Object
Extend belongs_to so that it 1.
- #constraint(foreign_key_column, parent_table_name: nil, constraint_name: nil, parent_class_name: nil, dependent: nil) ⇒ Object
-
#declare_field(name, type, *args, **options) ⇒ Object
Declare named field with a type and an arbitrary set of arguments.
-
#ignore_index(index_name) ⇒ Object
tell the migration generator to ignore the named index.
- #index(columns, name: nil, allow_equivalent: true, ignore_equivalent_definitions: false, unique: false, where: nil, length: nil) ⇒ Object
- #index_definitions_with_primary_key ⇒ Object
- #primary_key_index(*columns) ⇒ Object
Instance Method Details
#_declared_primary_key ⇒ Object
returns the primary key (String) as declared with primary_key = unlike the ‘primary_key` method, DOES NOT query the database to find the actual primary key in use right now if no explicit primary key set, returns the _default_declared_primary_key
266 267 268 269 270 271 272 273 274 |
# File 'lib/declare_schema/model.rb', line 266 def _declared_primary_key if !defined?(@primary_key) || (ActiveSupport.version >= Gem::Version.new('7.1.0') && @primary_key == ActiveRecord::AttributeMethods::PrimaryKey::ClassMethods::PRIMARY_KEY_NOT_SET) _default_declared_primary_key else @primary_key&.to_s end end |
#_infer_fk_limit(foreign_key_column, reflection) ⇒ Object
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
# File 'lib/declare_schema/model.rb', line 241 def _infer_fk_limit(foreign_key_column, reflection) if reflection.[:polymorphic] if (foreign_key_column = _column(foreign_key_column)) && foreign_key_column.type == :integer foreign_key_column.limit end else klass = reflection.klass or raise "Couldn't find belongs_to klass for #{name} in #{reflection.inspect}" if (pk_id_type = klass.&.[](:id)) if pk_id_type == :integer 4 end else if klass.table_exists? && (pk_column = klass.columns_hash[klass._declared_primary_key]) pk_id_type = pk_column.type if pk_id_type == :integer pk_column.limit end end end end end |
#attr_type(name) ⇒ Object
Returns the type (a class) for a given field or association. If the association is a collection (has_many or habtm) return the AssociationReflection instead
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 |
# File 'lib/declare_schema/model.rb', line 383 public \ def attr_type(name) if attr_types.nil? && self != self.name.constantize raise "attr_types called on a stale class object (#{self.name}). Avoid storing persistent references to classes" end attr_types[name] || if (reflection = reflections[name.to_s]) if reflection.macro.in?([:has_one, :belongs_to]) && !reflection.[:polymorphic] reflection.klass else reflection end end || if (col = _column(name.to_s)) DeclareSchema::PLAIN_TYPES[col.type] || col.klass end end |
#belongs_to(name, scope = nil, **options) ⇒ Object
Extend belongs_to so that it
-
creates a FieldSpec for the foreign key
-
declares an index on the foreign key (optional)
-
declares a foreign_key constraint (optional)
Other options are passed through to super
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/declare_schema/model.rb', line 138 def belongs_to(name, scope = nil, **) if [:null].in?([true, false]) && [:optional] == [:null] STDERR.puts("[declare_schema warning] belongs_to #{name.inspect}, null: with the same value as optional: is redundant; omit null: #{[:null]} (called from #{caller[0]})") elsif !.has_key?(:optional) case [:null] when true STDERR.puts("[declare_schema] belongs_to #{name.inspect}, null: true is deprecated in favor of optional: true (called from #{caller[0]})") when false STDERR.puts("[declare_schema] belongs_to #{name.inspect}, null: false is implied and can be omitted (called from #{caller[0]})") end end = {} [:null] = if .has_key?(:null) .delete(:null) elsif .has_key?(:optional) [:optional] # infer :null from :optional end || false [:default] = .delete(:default) if .has_key?(:default) if .has_key?(:limit) limit = .delete(:limit) DeclareSchema.deprecator.warn("belongs_to #{name.inspect}, limit: #{limit} is deprecated since it is now inferred") end # index: true means create an index on the foreign key # index: false means do not create an index on the foreign key # index: { ... } means create an index on the foreign key with the given options index_value = .delete(:index) if index_value == false # don't create an index .delete(:unique) .delete(:allow_equivalent) else = {} # create an index case index_value when String, Symbol DeclareSchema.deprecator.warn("[declare_schema] belongs_to #{name.inspect}, index: 'name' is deprecated; use index: { name: 'name' } instead (in #{self.name})") [:name] = index_value.to_s when true when nil when Hash = index_value else raise ArgumentError, "[declare_schema] belongs_to #{name.inspect}, index: must be true or false or a Hash; got #{index_value.inspect} (in #{self.name})" end if .has_key?(:unique) DeclareSchema.deprecator.warn("[declare_schema] belongs_to #{name.inspect}, unique: true|false is deprecated; use index: { unique: true|false } instead (in #{self.name})") [:unique] = .delete(:unique) end [:allow_equivalent] = .delete(:allow_equivalent) if .has_key?(:allow_equivalent) end constraint_name = .delete(:constraint) dependent_delete = :delete if .delete(:far_end_dependent) == :delete # infer :optional from :null if !.has_key?(:optional) [:optional] = [:null] end super reflection = reflections[name.to_s] or raise "Couldn't find reflection #{name} in #{reflections.keys}" foreign_key_column = reflection.foreign_key or raise "Couldn't find foreign_key for #{name} in #{reflection.inspect}" = .dup # Note: the foreign key limit: should match the primary key limit:. (If there is a foreign key constraint, # those limits _must_ match.) We'd like to call _infer_fk_limit and get the limit right from the PK. # But we can't here, because that will mess up the autoloader to follow every belongs_to association right # when it is declared. So instead we assume :bigint (integer limit: 8) below, while also registering this # pre_migration: callback to double-check that assumption Just In Time--right before we generate a migration. # # The one downside of this approach is that application code that asks the field_spec for the declared # foreign key limit: will always get 8 back even if this is a grandfathered foreign key that points to # a limit: 4 primary key. It seems unlikely that any application code would do this. [:pre_migration] = ->(field_spec) do if (inferred_limit = _infer_fk_limit(foreign_key_column, reflection)) field_spec.[:limit] = inferred_limit end end declare_field(foreign_key_column.to_sym, :bigint, **) if reflection.[:polymorphic] foreign_type = [:foreign_type] || "#{name}_type" _declare_polymorphic_type_field(foreign_type, ) if ::DeclareSchema.default_generate_indexing && index([foreign_type, foreign_key_column], **) end else if ::DeclareSchema.default_generate_indexing && index([foreign_key_column], **) end if ::DeclareSchema.default_generate_foreign_keys && constraint_name != false constraint(foreign_key_column, constraint_name: constraint_name || &.[](:name), parent_class_name: reflection.class_name, dependent: dependent_delete) end end end |
#constraint(foreign_key_column, parent_table_name: nil, constraint_name: nil, parent_class_name: nil, dependent: nil) ⇒ Object
76 77 78 79 80 81 82 83 84 |
# File 'lib/declare_schema/model.rb', line 76 def constraint(foreign_key_column, parent_table_name: nil, constraint_name: nil, parent_class_name: nil, dependent: nil) constraint_definition = ::DeclareSchema::Model::ForeignKeyDefinition.new( foreign_key_column.to_s, constraint_name: constraint_name, child_table_name: table_name, parent_table_name: parent_table_name, parent_class_name: parent_class_name, dependent: dependent ) constraint_definitions << constraint_definition # Set<> implements idempotent insert. end |
#declare_field(name, type, *args, **options) ⇒ Object
Declare named field with a type and an arbitrary set of arguments. The arguments are forwarded to the #field_added callback, allowing custom metadata to be added to field declarations.
96 97 98 99 100 101 102 103 104 105 |
# File 'lib/declare_schema/model.rb', line 96 def declare_field(name, type, *args, **) try(:field_added, name, type, args, ) _add_serialize_for_field(name, type, ) _add_formatting_for_field(name, type) _add_validations_for_field(name, type, args, ) _add_index_for_field(name, args, **) _add_scopes_for_field(name, type, **) field_specs[name] = ::DeclareSchema::Model::FieldSpec.new(self, name, type, position: field_specs.size, **) attr_order << name unless attr_order.include?(name) end |
#ignore_index(index_name) ⇒ Object
tell the migration generator to ignore the named index. Useful for existing indexes, or for indexes that can’t be automatically generated.
88 89 90 |
# File 'lib/declare_schema/model.rb', line 88 def ignore_index(index_name) ignore_indexes << index_name.to_s end |
#index(columns, name: nil, allow_equivalent: true, ignore_equivalent_definitions: false, unique: false, where: nil, length: nil) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/declare_schema/model.rb', line 52 def index(columns, name: nil, allow_equivalent: true, ignore_equivalent_definitions: false, unique: false, where: nil, length: nil) index_definition = ::DeclareSchema::Model::IndexDefinition.new( columns, name: name, table_name: table_name, allow_equivalent: allow_equivalent, unique: unique, where: where, length: length ) if (equivalent = index_definitions.find { index_definition.equivalent?(_1) }) # differs only by name if equivalent == index_definition # identical is always idempotent else # equivalent is idempotent iff ignore_equivalent_definitions: true passed ignore_equivalent_definitions or raise ArgumentError, "equivalent index definition found (pass ignore_equivalent_definitions: true to ignore):\n" \ "#{index_definition.inspect}\n#{equivalent.inspect}" end else index_definitions << index_definition end end |
#index_definitions_with_primary_key ⇒ Object
107 108 109 110 111 112 113 |
# File 'lib/declare_schema/model.rb', line 107 def index_definitions_with_primary_key if index_definitions.any?(&:primary_key?) index_definitions else index_definitions + [_rails_default_primary_key] end end |
#primary_key_index(*columns) ⇒ Object
72 73 74 |
# File 'lib/declare_schema/model.rb', line 72 def primary_key_index(*columns) index(columns.flatten, unique: true, name: ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME) end |