modalfields 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.0
1
+ 1.4.0
@@ -321,8 +321,115 @@ module ModalFields
321
321
  true
322
322
  end
323
323
 
324
+ def report(options={})
325
+ dbmodels(dbmodel_options).each do |model, file|
326
+ if options[:tables]
327
+ yield :table, model.table_name, nil, {:model=>model}
328
+ end
329
+
330
+ submodels = model.send(:subclasses)
331
+ existing_fields, association_fields, pk_fields = model_existing_fields(model, submodels)
332
+ unless file.nil?
333
+ pre, start_fields, fields, end_fields, post = split_model_file(file)
334
+ else
335
+ end
336
+
337
+ pks = pk_fields.map{|pk_field_name| existing_fields.find{|f| f.name==pk_field_name}}
338
+ existing_fields.reject!{|f| f.name.in? pk_fields}
339
+ if options[:primary_keys]
340
+ pks.each do |pk_field|
341
+ yield :primary_key, model.table_name, pk_field.name, field_data(pk_field)
342
+ end
343
+ end
344
+
345
+ assoc_cols = []
346
+ association_fields.each do |assoc, cols|
347
+ if options[:associations]
348
+ if assoc.options[:polymorphic]
349
+ foreign_table = :polymorphic
350
+ else
351
+ foreign_table = assoc.klass.table_name
352
+ end
353
+ yield :association, model.table_name, assoc.name, {:foreign_table=>foreign_table}
354
+ end
355
+ Array(cols).each do |col|
356
+ col = existing_fields.find{|f| f.name.to_s==col.to_s}
357
+ assoc_cols << col
358
+ if options[:foreign_keys]
359
+ yield :foreign_key, model.table_name, col.name, field_data(col, :assoc=>assoc)
360
+ end
361
+ end
362
+ end
363
+ existing_fields -= assoc_cols
364
+
365
+ fields = Array(fields).reject{|line, name, comment| name.blank?}
366
+ if model.respond_to?(:fields_info)
367
+ field_order = model.fields_info.map(&:name).map(&:to_s) & existing_fields.map(&:name)
368
+ else
369
+ field_order = []
370
+ end
371
+ if options[:undeclared_fields]
372
+ field_order += existing_fields.map(&:name).reject{|name| field_order.include?(name.to_s)}
373
+ end
374
+ field_comments = Hash[fields.map{|line, name, comment| [name,comment]}]
375
+ field_extras = Hash[ model.fields_info.map{|fi| [fi.name.to_s,fi.attributes]}]
376
+ field_order.each do |field_name|
377
+ field_info = existing_fields.find{|f| f.name.to_s==field_name}
378
+ field_comment = field_comments[field_name]
379
+ field_extra = field_extras[field_name]
380
+ if field_info.blank?
381
+ raise " MISSING FIELD: #{field_name} (#{model})"
382
+ else
383
+ yield :field, model.table_name, field_info.name, field_data(field_info, :comments=>field_comment, :extra=>field_extra)
384
+ end
385
+ end
386
+
387
+ end
388
+ end
389
+
324
390
  private
325
391
 
392
+ def field_data(field_info, options={})
393
+ comments = options[:comments]
394
+ extra = options[:extra]
395
+ assoc = options[:assoc]
396
+ attrs = [:type, :sql_type, :default, :null]
397
+ data = {}
398
+ if comments.present?
399
+ comments = comments.strip
400
+ comments = comments[1..-1] if comments.starts_with?('#')
401
+ comments = comments.strip
402
+ end
403
+ data[:comments] = comments if comments.present?
404
+ if assoc.present?
405
+ data[:foreign_name] = assoc.name
406
+ if assoc.options[:polymorphic]
407
+ data[:foreign_table] = :polymorphic
408
+ else
409
+ data[:foreign_table] = assoc.klass.table_name
410
+ end
411
+ end
412
+ # case field_info.type
413
+ # when :string, :text, :binary, :integer
414
+ # attrs << :limit
415
+ # when :decimal
416
+ # attrs << :scale << :precision
417
+ # when :geometry
418
+ # attrs << :srid
419
+ # end
420
+ attrs += ModalFields.definitions[field_info.type].keys
421
+ attrs = attrs.uniq
422
+ if extra.present?
423
+ extra = extra.except(*attrs)
424
+ end
425
+ attrs.each do |attr|
426
+ v = field_info.send(attr)
427
+ data[attr] = v if v.present?
428
+ end
429
+ data[:extra] = extra if extra.present?
430
+ data
431
+ end
432
+
326
433
  # Return the database models
327
434
  # Options:
328
435
  # :all_models # Return also models in plugins, not only in the app (app/models)
@@ -336,16 +443,16 @@ module ModalFields
336
443
 
337
444
  models_dir = 'app/models'
338
445
  if Rails.respond_to?(:application)
339
- models_dir = Rails.application.paths[models_dir]
446
+ model_dirs = Rails.application.paths[models_dir]
447
+ else
448
+ model_dirs = [models_dir]
340
449
  end
341
450
  models_dir = Rails.root.join(models_dir)
451
+ model_dirs = model_dirs.map{|d| Rails.root.join(d)}
342
452
 
343
453
  if options[:all_models]
344
454
  # Include also models from plugins
345
455
  model_dirs = $:.grep(/\/models\/?\Z/)
346
- else
347
- # Only main application models
348
- model_dirs = [models_dir]
349
456
  end
350
457
 
351
458
  models = []
@@ -427,24 +534,10 @@ module ModalFields
427
534
  # declarations.
428
535
  # * deleted_fields are fields declared in the fields block but not present in the current schema.
429
536
  def diff(model)
430
- # model.columns will fail if the table does not exist
431
- existing_fields = model.columns rescue []
432
- deleted_model = existing_fields.empty?
433
537
  submodels = model.send(:subclasses)
434
- assocs = model.reflect_on_all_associations(:belongs_to) +
435
- submodels.map{|sc| sc.reflect_on_all_associations(:belongs_to)}.flatten
436
- association_fields = assocs.map{ |r|
437
- # up to ActiveRecord 3.1 we had primary_key_name in AssociationReflection; now it's foreign_key
438
- cols = [r.respond_to?(:primary_key_name) ? r.primary_key_name : r.foreign_key]
439
- if r.options[:polymorphic]
440
- t = r.options[:foreign_type]
441
- t ||= r.foreign_type if r.respond_to?(:foreign_type)
442
- t ||= cols.first.sub(/_id\Z/,'_type')
443
- cols << t
444
- end
445
- cols
446
- }.flatten.map(&:to_s)
447
- pk_fields = Array(model.primary_key).map(&:to_s)
538
+ existing_fields, association_fields, pk_fields = model_existing_fields(model, submodels)
539
+ association_fields = association_fields.map{|assoc_name, cols| cols}.flatten.map(&:to_s)
540
+ deleted_model = existing_fields.empty?
448
541
  case show_primary_keys
449
542
  when true
450
543
  pk_fields = []
@@ -617,6 +710,30 @@ module ModalFields
617
710
  fields_info.kind_of?(Array) ? fields_info : nil
618
711
  end
619
712
 
713
+ # Returns three arrays:
714
+ # * array of existing fields (column info objects)
715
+ # * array of association fields: each element is the association information followed by the foreign key fields
716
+ # * array of primary key field names
717
+ def model_existing_fields(model, submodels=[])
718
+ # model.columns will fail if the table does not exist
719
+ existing_fields = model.columns rescue []
720
+ assocs = model.reflect_on_all_associations(:belongs_to) +
721
+ submodels.map{|sc| sc.reflect_on_all_associations(:belongs_to)}.flatten
722
+ association_fields = assocs.map{ |r|
723
+ # up to ActiveRecord 3.1 we had primary_key_name in AssociationReflection; now it's foreign_key
724
+ cols = [r.respond_to?(:primary_key_name) ? r.primary_key_name : r.foreign_key]
725
+ if r.options[:polymorphic]
726
+ t = r.options[:foreign_type]
727
+ t ||= r.foreign_type if r.respond_to?(:foreign_type)
728
+ t ||= cols.first.sub(/_id\Z/,'_type')
729
+ cols << t
730
+ end
731
+ [r, cols]
732
+ }
733
+ pk_fields = Array(model.primary_key).map(&:to_s)
734
+ [existing_fields, association_fields, pk_fields]
735
+ end
736
+
620
737
  end
621
738
 
622
739
 
@@ -0,0 +1,36 @@
1
+ namespace :fields do
2
+
3
+ require 'csv'
4
+
5
+ desc "Dump fields information in CSV"
6
+ task :csv=>:environment do
7
+ fn = Rails.root.join('tmp','fields.csv')
8
+ if CSV.constants.map(&:to_sym).include?(:VERSION)
9
+ options = [{
10
+ :headers=>true,
11
+ :col_sep=>';'
12
+ }]
13
+ else
14
+ options = [';']
15
+ end
16
+ CSV.open(fn, 'w', *options) do |csv|
17
+ csv << %w{table column pk assoc foreign sql_type type attributes extra comments}
18
+ ModalFields.report(
19
+ :primary_keys=>true, :foreing_keys=>true,
20
+ :undeclared_fields=>true) do |kind, table, name, data|
21
+ row_data = [table, name, kind==:primary_key]
22
+ if kind==:foreign_key
23
+ row_data << data[:foreign_name] << data[:foreign_table].to_s
24
+ else
25
+ row_data << '' << ''
26
+ end
27
+ attrs = data.except(:sql_type, :type, :extra, :comments).to_json
28
+ extra = data[:extra]
29
+ row_data << data[:sql_type] << data[:type] << attrs << data[:extra] << data[:comments]
30
+ csv << row_data
31
+ end
32
+ end
33
+ puts "Data written to #{fn}"
34
+ end
35
+
36
+ end
@@ -0,0 +1,24 @@
1
+ namespace :fields do
2
+
3
+ desc "Report the current schema"
4
+ task :report=>:environment do
5
+ ModalFields.report(
6
+ :tables=>true, :primary_keys=>true, :foreign_keys=>true, :associations=>true,
7
+ :undeclared_fields=>true) do |kind, table, name, data|
8
+ case kind
9
+ when :table
10
+ puts "="*50
11
+ puts table
12
+ when :association
13
+ puts " Foreign keys for #{name} (table: #{data[:foreign_table]})"
14
+ when :primary_key
15
+ puts " Primary key: #{name} #{data[:sql_type]}"
16
+ when :foreign_key
17
+ puts " #{name} #{data[:sql_type]}"
18
+ else
19
+ puts " #{name} #{data[:sql_type]}"
20
+ end
21
+ end
22
+ end
23
+
24
+ end
data/modalfields.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "modalfields"
8
- s.version = "1.3.0"
8
+ s.version = "1.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Javier Goizueta"]
12
- s.date = "2012-09-06"
12
+ s.date = "2012-09-17"
13
13
  s.description = "ModelFields is a Rails plugin that adds fields declarations to your models."
14
14
  s.email = "jgoizueta@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -31,8 +31,10 @@ Gem::Specification.new do |s|
31
31
  "lib/modalfields/standardfields.rb",
32
32
  "lib/modalfields/tasks.rb",
33
33
  "lib/tasks/check.rake",
34
+ "lib/tasks/csv.rake",
34
35
  "lib/tasks/migrate.rake",
35
36
  "lib/tasks/migration.rake",
37
+ "lib/tasks/report.rake",
36
38
  "lib/tasks/update.rake",
37
39
  "modalfields.gemspec",
38
40
  "test/create_database.rb",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: modalfields
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-06 00:00:00.000000000 Z
12
+ date: 2012-09-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: modalsettings
@@ -193,8 +193,10 @@ files:
193
193
  - lib/modalfields/standardfields.rb
194
194
  - lib/modalfields/tasks.rb
195
195
  - lib/tasks/check.rake
196
+ - lib/tasks/csv.rake
196
197
  - lib/tasks/migrate.rake
197
198
  - lib/tasks/migration.rake
199
+ - lib/tasks/report.rake
198
200
  - lib/tasks/update.rake
199
201
  - modalfields.gemspec
200
202
  - test/create_database.rb
@@ -230,7 +232,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
230
232
  version: '0'
231
233
  segments:
232
234
  - 0
233
- hash: -375049972268577712
235
+ hash: 1905351546743717120
234
236
  required_rubygems_version: !ruby/object:Gem::Requirement
235
237
  none: false
236
238
  requirements: