modalfields 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/modalfields/modalfields.rb +138 -21
- data/lib/tasks/csv.rake +36 -0
- data/lib/tasks/report.rake +24 -0
- data/modalfields.gemspec +4 -2
- metadata +5 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
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
|
-
|
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
|
-
|
435
|
-
|
436
|
-
|
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
|
|
data/lib/tasks/csv.rake
ADDED
@@ -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.
|
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-
|
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.
|
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-
|
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:
|
235
|
+
hash: 1905351546743717120
|
234
236
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
235
237
|
none: false
|
236
238
|
requirements:
|