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 +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:
         |