Module: ModalFields
- Defined in:
- lib/modalfields/modalfields.rb
Overview
This is a hybrid between HoboFields and model annotators.
It works like other annotators, by updating the model annotations from the DB schema. But the annotations are syntactic Ruby as in HoboFields rather than comments.
Apart from looking better to my eyes, this allows triggering special functionality from the field declations (such as specifying validations).
The annotations are kept up to date by the migration tasks. Comments and validation, etc. specifications modified manually are preserved, at least if the field block syntax is kept as generated (one line per field, one line for the block start and end…)
Custom type fields and hooks can be define in files (e.g. fields.rb) in config/initializers/
Defined Under Namespace
Modules: FieldDeclarationClassMethods Classes: DeclarationsDsl, DefinitionsDsl, DslBase, FieldDeclaration, HooksDsl
Constant Summary collapse
- SPECIFIERS =
[:indexed, :unique, :required]
- COMMON_ATTRIBUTES =
{:default=>nil, :null=>true}
Class Attribute Summary collapse
-
.definitions ⇒ Object
readonly
Returns the value of attribute definitions.
-
.hooks ⇒ Object
readonly
Returns the value of attribute hooks.
-
.migration_hooks ⇒ Object
readonly
Returns the value of attribute migration_hooks.
-
.show_primary_keys ⇒ Object
Define declaration of primary keys ModalFields.show_primary_keys = false # the default: do not show primary keys ModalFields.show_primary_keys = true # always declare primary keys ModalFields.show_primary_keys = :id # only declare if named ‘id’ (otherwise the model will have a primary_key declaration) ModalFields.show_primary_keys = :except_id # only declare if named differently from ‘id’.
Class Method Summary collapse
-
.alias(aliases) ⇒ Object
Define type synonyms.
- .check ⇒ Object
-
.column_to_field_declaration(&blk) ⇒ Object
Define a custom column to field declaration conversion.
-
.define(&blk) ⇒ Object
Run a definition block that executes field type definitions.
-
.enable ⇒ Object
Enable the ModalFields plugin (adds the fields declarator to model classes).
-
.hook(&blk) ⇒ Object
Run a hooks block that defines field declaration processors.
- .migration ⇒ Object
- .models(options = nil) ⇒ Object
- .parameters(params = nil) ⇒ Object
- .report(options = {}) ⇒ Object
-
.update(modify = true) ⇒ Object
Update the field declarations of all the models.
- .validate(declaration) ⇒ Object
Class Attribute Details
.definitions ⇒ Object (readonly)
Returns the value of attribute definitions.
169 170 171 |
# File 'lib/modalfields/modalfields.rb', line 169 def definitions @definitions end |
.hooks ⇒ Object (readonly)
Returns the value of attribute hooks.
169 170 171 |
# File 'lib/modalfields/modalfields.rb', line 169 def hooks @hooks end |
.migration_hooks ⇒ Object (readonly)
Returns the value of attribute migration_hooks.
169 170 171 |
# File 'lib/modalfields/modalfields.rb', line 169 def migration_hooks @migration_hooks end |
.show_primary_keys ⇒ Object
Define declaration of primary keys
ModalFields.show_primary_keys = false # the default: do not show primary keys
ModalFields.show_primary_keys = true # always declare primary keys
ModalFields.show_primary_keys = :id # only declare if named 'id' (otherwise the model will have a primary_key declaration)
ModalFields.show_primary_keys = :except_id # only declare if named differently from 'id'
175 176 177 |
# File 'lib/modalfields/modalfields.rb', line 175 def show_primary_keys @show_primary_keys end |
Class Method Details
.alias(aliases) ⇒ Object
Define type synonyms
188 189 190 |
# File 'lib/modalfields/modalfields.rb', line 188 def alias(aliases) @type_aliases.merge! aliases end |
.check ⇒ Object
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
# File 'lib/modalfields/modalfields.rb', line 252 def check dbmodels().each do |model, file| new_fields, modified_fields, deleted_fields, deleted_model = diff(model) unless new_fields.empty? && modified_fields.empty? && deleted_fields.empty? rel_file = file && file.sub(/\A#{Rails.root}/,'') puts "#{model} (#{rel_file}):" puts " (deleted)" if deleted_model [['+',new_fields],['*',modified_fields],['-',deleted_fields]].each do |prefix, fields| puts fields.map{|field| " #{prefix} #{field}"}*"\n" unless fields.empty? # TODO: report index differences end puts "" end end end |
.column_to_field_declaration(&blk) ⇒ Object
Define a custom column to field declaration conversion
198 199 200 |
# File 'lib/modalfields/modalfields.rb', line 198 def column_to_field_declaration(&blk) @column_to_field_declaration_hook = blk end |
.define(&blk) ⇒ Object
Run a definition block that executes field type definitions
183 184 185 |
# File 'lib/modalfields/modalfields.rb', line 183 def define(&blk) DefinitionsDsl.new.instance_eval(&blk) end |
.enable ⇒ Object
Enable the ModalFields plugin (adds the fields declarator to model classes)
203 204 205 206 207 208 209 210 |
# File 'lib/modalfields/modalfields.rb', line 203 def enable if defined?(::Rails) # class ::ActiveRecord::Base # extend FieldDeclarationClassMethods # end ::ActiveRecord::Base.send :extend, FieldDeclarationClassMethods end end |
.hook(&blk) ⇒ Object
Run a hooks block that defines field declaration processors
193 194 195 |
# File 'lib/modalfields/modalfields.rb', line 193 def hook(&blk) HooksDsl.new.instance_eval(&blk) end |
.migration ⇒ Object
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
# File 'lib/modalfields/modalfields.rb', line 268 def migration up = "" down = "" dbmodels().each do |model, file| new_fields, modified_fields, deleted_fields, deleted_model = diff(model).map{|fields| fields.kind_of?(Array) ? fields.map{|f| migration_declaration(model, f)} : fields } unless new_fields.empty? && modified_fields.empty? && deleted_fields.empty? up << "\n" down << "\n" rel_file = file && file.sub(/\A#{Rails.root}/,'') if deleted_model && modified_fields.empty? && new_fields.empty? up << " create_table #{model.table_name.to_sym.inspect} do |t|\n" deleted_fields.each do |field| up << " t.#{field.type} #{field.name.inspect}" unless field.attributes.empty? up << ", " + field.attributes.inspect.unwrap('{}') end up << "\n" end up << " end\n" down << " drop_table #{model.table_name.to_sym.inspect}\n" else deleted_fields.each do |field| up << " add_column #{model.table_name.to_sym.inspect}, #{field.name.inspect}, #{field.type.inspect}" unless field.attributes.empty? up << ", " + field.attributes.inspect.unwrap('{}') end up << "\n" down << " remove_column #{model.table_name.to_sym.inspect}, #{field.name.inspect}\n" end modified_fields.each do |field| changed = model.fields_info.find{|f| f.name.to_sym==field.name.to_sym} changed &&= migration_declaration(model, changed) up << " change_column #{model.table_name.to_sym.inspect}, #{changed.name.inspect}, #{changed.type.inspect}" unless changed.attributes.empty? up << ", " + changed.attributes.inspect.unwrap('{}') end up << "\n" down << " change_column #{model.table_name.to_sym.inspect}, #{field.name.inspect}, #{field.type.inspect}" unless field.attributes.empty? down << ", " + field.attributes.inspect.unwrap('{}') end down << "\n" end new_fields.each do |field| up << " remove_column #{model.table_name.to_sym.inspect}, #{field.name.inspect}\n" down << " add_column #{model.table_name.to_sym.inspect}, #{field.name.inspect}, #{field.type.inspect}" unless field.attributes.empty? down << ", " + field.attributes.inspect.unwrap('{}') end down << "\n" end end # TODO: indices end end unless up.blank? puts "\n\# up:" puts up end unless down.blank? puts "\n\# down:" puts down end puts "" end |
.models(options = nil) ⇒ Object
415 416 417 |
# File 'lib/modalfields/modalfields.rb', line 415 def models(=nil) dbmodels || end |
.parameters(params = nil) ⇒ Object
177 178 179 180 |
# File 'lib/modalfields/modalfields.rb', line 177 def parameters(params=nil) @parameters.merge! params if params @parameters end |
.report(options = {}) ⇒ Object
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
# File 'lib/modalfields/modalfields.rb', line 344 def report(={}) models = Array([:model]) models = dbmodels() if models.blank? models.each do |model, file| if [:tables] yield :table, model.table_name, nil, {:model=>model} end submodels = model.send(:subclasses) existing_fields, association_fields, pk_fields = model_existing_fields(model, submodels) unless file.nil? pre, start_fields, fields, end_fields, post = split_model_file(file) else end pks = pk_fields.map{|pk_field_name| existing_fields.find{|f| f.name==pk_field_name}} existing_fields = existing_fields.reject{|f| f.name.in? pk_fields} if [:primary_keys] pks.each do |pk_field| yield :primary_key, model.table_name, pk_field.name, field_data(pk_field) end end assoc_cols = [] association_fields.each do |assoc, cols| if [:associations] if assoc.[:polymorphic] foreign_table = :polymorphic else foreign_table = assoc.klass.table_name end yield :association, model.table_name, assoc.name, {:foreign_table=>foreign_table} end Array(cols).each do |col| col = existing_fields.find{|f| f.name.to_s==col.to_s} next unless col assoc_cols << col if [:foreign_keys] yield :foreign_key, model.table_name, col.name, field_data(col, :assoc=>assoc) end end end existing_fields -= assoc_cols has_fields_info = model.respond_to?(:fields_info) && model.fields_info != :omitted fields = Array(fields).reject{|line, name, comment| name.blank?} if has_fields_info field_order = model.fields_info.map(&:name).map(&:to_s) & existing_fields.map(&:name) else field_order = [] end if [:undeclared_fields] field_order += existing_fields.map(&:name).reject{|name| field_order.include?(name.to_s)} end field_comments = Hash[fields.map{|line, name, comment| [name,comment]}] field_extras = has_fields_info ? Hash[ model.fields_info.map{|fi| [fi.name.to_s,fi.attributes]}] : {} field_order.each do |field_name| field_info = existing_fields.find{|f| f.name.to_s==field_name} field_comment = field_comments[field_name] field_extra = field_extras[field_name] if field_info.blank? raise " MISSING FIELD: #{field_name} (#{model})" else yield :field, model.table_name, field_info.name, field_data(field_info, :comments=>field_comment, :extra=>field_extra) end end end end |
.update(modify = true) ⇒ Object
Update the field declarations of all the models. This modifies the source files of all the models (touches only the fields block or adds one if not present). It is recommended to run this on a clearn working directory (no uncommitted changes), so that the changes can be easily reviewed.
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/modalfields/modalfields.rb', line 216 def update(modify=true) dbmodels().each do |model, file| next if file.nil? model.reset_column_information new_fields, modified_fields, deleted_fields, deleted_model = diff(model) unless new_fields.empty? && modified_fields.empty? && deleted_fields.empty? pre, start_fields, fields, end_fields, post = split_model_file(file) deleted_names = deleted_fields.map{|f| f.name.to_s} fields = fields.reject{|line, name, comment| deleted_names.include?(name)} fields = fields.map{|line, name, comment| mod_field = modified_fields.detect{|f| f.name.to_s==name} if mod_field line = " "+mod_field.to_s line << " #{comment}" if comment line << "\n" end [line, name, comment] } pk_names = Array(model.primary_key).map(&:to_s) created_at = new_fields.detect{|f| f.name.to_s=='created_at'} updated_at = new_fields.detect{|f| f.name.to_s=='updated_at'} if created_at && updated_at && created_at.type.to_sym==:datetime && updated_at.type.to_sym==:datetime = true new_fields -= [created_at, updated_at] end fields += new_fields.map{|f| comments = pk_names.include?(f.name.to_s) ? " \# PK" : "" [" #{f}#{comments}\n" ] } fields << [" timestamps\n"] if output_file = modify ? file : "#{file}_with_fields.rb" join_model_file(output_file, pre, start_fields, fields, end_fields, post) end end end |
.validate(declaration) ⇒ Object
336 337 338 339 340 341 342 |
# File 'lib/modalfields/modalfields.rb', line 336 def validate(declaration) definition = definitions[declaration.type.to_sym] raise "Field type #{declaration.type} not defined (#{declaration.inspect})" unless definition # TODO: validate declaration.specifiers # TODO: validate declaration.attributes with definition true end |