diff options
Diffstat (limited to 'activerecord/lib/rails')
10 files changed, 313 insertions, 0 deletions
diff --git a/activerecord/lib/rails/generators/active_record.rb b/activerecord/lib/rails/generators/active_record.rb new file mode 100644 index 0000000000..a7e5e373a7 --- /dev/null +++ b/activerecord/lib/rails/generators/active_record.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require "rails/generators/named_base" +require "rails/generators/active_model" +require "rails/generators/active_record/migration" +require "active_record" + +module ActiveRecord + module Generators # :nodoc: + class Base < Rails::Generators::NamedBase # :nodoc: + include ActiveRecord::Generators::Migration + + # Set the current directory as base for the inherited generators. + def self.base_root + __dir__ + end + end + end +end diff --git a/activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb b/activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb new file mode 100644 index 0000000000..35d5664400 --- /dev/null +++ b/activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require "rails/generators/active_record" + +module ActiveRecord + module Generators # :nodoc: + class ApplicationRecordGenerator < ::Rails::Generators::Base # :nodoc: + source_root File.expand_path("templates", __dir__) + + # FIXME: Change this file to a symlink once RubyGems 2.5.0 is required. + def create_application_record + template "application_record.rb", application_record_file_name + end + + private + + def application_record_file_name + @application_record_file_name ||= + if namespaced? + "app/models/#{namespaced_path}/application_record.rb" + else + "app/models/application_record.rb" + end + end + end + end +end diff --git a/activerecord/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt b/activerecord/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt new file mode 100644 index 0000000000..60050e0bf8 --- /dev/null +++ b/activerecord/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt @@ -0,0 +1,5 @@ +<% module_namespacing do -%> +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end +<% end -%> diff --git a/activerecord/lib/rails/generators/active_record/migration.rb b/activerecord/lib/rails/generators/active_record/migration.rb new file mode 100644 index 0000000000..cbb88d571d --- /dev/null +++ b/activerecord/lib/rails/generators/active_record/migration.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require "rails/generators/migration" + +module ActiveRecord + module Generators # :nodoc: + module Migration + extend ActiveSupport::Concern + include Rails::Generators::Migration + + module ClassMethods + # Implement the required interface for Rails::Generators::Migration. + def next_migration_number(dirname) + next_migration_number = current_migration_number(dirname) + 1 + ActiveRecord::Migration.next_migration_number(next_migration_number) + end + end + + private + + def primary_key_type + key_type = options[:primary_key_type] + ", id: :#{key_type}" if key_type + end + + def db_migrate_path + if defined?(Rails.application) && Rails.application + configured_migrate_path || default_migrate_path + else + "db/migrate" + end + end + + def default_migrate_path + Rails.application.config.paths["db/migrate"].to_ary.first + end + + def configured_migrate_path + return unless database = options[:database] + config = ActiveRecord::Base.configurations.configs_for( + env_name: Rails.env, + spec_name: database, + ) + config&.migrations_paths + end + end + end +end diff --git a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb new file mode 100644 index 0000000000..dd79bcf542 --- /dev/null +++ b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require "rails/generators/active_record" + +module ActiveRecord + module Generators # :nodoc: + class MigrationGenerator < Base # :nodoc: + argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]" + + class_option :primary_key_type, type: :string, desc: "The type for primary key" + class_option :database, type: :string, aliases: %i(db), desc: "The database for your migration. By default, the current environment's primary database is used." + + def create_migration_file + set_local_assigns! + validate_file_name! + migration_template @migration_template, File.join(db_migrate_path, "#{file_name}.rb") + end + + private + attr_reader :migration_action, :join_tables + + # Sets the default migration template that is being used for the generation of the migration. + # Depending on command line arguments, the migration template and the table name instance + # variables are set up. + def set_local_assigns! + @migration_template = "migration.rb" + case file_name + when /^(add)_.*_to_(.*)/, /^(remove)_.*?_from_(.*)/ + @migration_action = $1 + @table_name = normalize_table_name($2) + when /join_table/ + if attributes.length == 2 + @migration_action = "join" + @join_tables = pluralize_table_names? ? attributes.map(&:plural_name) : attributes.map(&:singular_name) + + set_index_names + end + when /^create_(.+)/ + @table_name = normalize_table_name($1) + @migration_template = "create_table_migration.rb" + end + end + + def set_index_names + attributes.each_with_index do |attr, i| + attr.index_name = [attr, attributes[i - 1]].map { |a| index_name_for(a) } + end + end + + def index_name_for(attribute) + if attribute.foreign_key? + attribute.name + else + attribute.name.singularize.foreign_key + end.to_sym + end + + def attributes_with_index + attributes.select { |a| !a.reference? && a.has_index? } + end + + # A migration file name can only contain underscores (_), lowercase characters, + # and numbers 0-9. Any other file name will raise an IllegalMigrationNameError. + def validate_file_name! + unless /^[_a-z0-9]+$/.match?(file_name) + raise IllegalMigrationNameError.new(file_name) + end + end + + def normalize_table_name(_table_name) + pluralize_table_names? ? _table_name.pluralize : _table_name.singularize + end + end + end +end diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt new file mode 100644 index 0000000000..5f7201cfe1 --- /dev/null +++ b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt @@ -0,0 +1,24 @@ +class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>] + def change + create_table :<%= table_name %><%= primary_key_type %> do |t| +<% attributes.each do |attribute| -%> +<% if attribute.password_digest? -%> + t.string :password_digest<%= attribute.inject_options %> +<% elsif attribute.token? -%> + t.string :<%= attribute.name %><%= attribute.inject_options %> +<% else -%> + t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %> +<% end -%> +<% end -%> +<% if options[:timestamps] %> + t.timestamps +<% end -%> + end +<% attributes.select(&:token?).each do |attribute| -%> + add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>, unique: true +<% end -%> +<% attributes_with_index.each do |attribute| -%> + add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %> +<% end -%> + end +end diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb.tt b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb.tt new file mode 100644 index 0000000000..481c70201b --- /dev/null +++ b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb.tt @@ -0,0 +1,46 @@ +class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>] +<%- if migration_action == 'add' -%> + def change +<% attributes.each do |attribute| -%> + <%- if attribute.reference? -%> + add_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %> + <%- elsif attribute.token? -%> + add_column :<%= table_name %>, :<%= attribute.name %>, :string<%= attribute.inject_options %> + add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>, unique: true + <%- else -%> + add_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %> + <%- if attribute.has_index? -%> + add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %> + <%- end -%> + <%- end -%> +<%- end -%> + end +<%- elsif migration_action == 'join' -%> + def change + create_join_table :<%= join_tables.first %>, :<%= join_tables.second %> do |t| + <%- attributes.each do |attribute| -%> + <%- if attribute.reference? -%> + t.references :<%= attribute.name %><%= attribute.inject_options %> + <%- else -%> + <%= '# ' unless attribute.has_index? -%>t.index <%= attribute.index_name %><%= attribute.inject_index_options %> + <%- end -%> + <%- end -%> + end + end +<%- else -%> + def change +<% attributes.each do |attribute| -%> +<%- if migration_action -%> + <%- if attribute.reference? -%> + remove_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %> + <%- else -%> + <%- if attribute.has_index? -%> + remove_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %> + <%- end -%> + remove_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %> + <%- end -%> +<%- end -%> +<%- end -%> + end +<%- end -%> +end diff --git a/activerecord/lib/rails/generators/active_record/model/model_generator.rb b/activerecord/lib/rails/generators/active_record/model/model_generator.rb new file mode 100644 index 0000000000..eac504f9f1 --- /dev/null +++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require "rails/generators/active_record" + +module ActiveRecord + module Generators # :nodoc: + class ModelGenerator < Base # :nodoc: + argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]" + + check_class_collision + + class_option :migration, type: :boolean + class_option :timestamps, type: :boolean + class_option :parent, type: :string, desc: "The parent class for the generated model" + class_option :indexes, type: :boolean, default: true, desc: "Add indexes for references and belongs_to columns" + class_option :primary_key_type, type: :string, desc: "The type for primary key" + class_option :database, type: :string, aliases: %i(db), desc: "The database for your model's migration. By default, the current environment's primary database is used." + + # creates the migration file for the model. + def create_migration_file + return unless options[:migration] && options[:parent].nil? + attributes.each { |a| a.attr_options.delete(:index) if a.reference? && !a.has_index? } if options[:indexes] == false + migration_template "../../migration/templates/create_table_migration.rb", File.join(db_migrate_path, "create_#{table_name}.rb") + end + + def create_model_file + template "model.rb", File.join("app/models", class_path, "#{file_name}.rb") + end + + def create_module_file + return if regular_class_path.empty? + template "module.rb", File.join("app/models", "#{class_path.join('/')}.rb") if behavior == :invoke + end + + hook_for :test_framework + + private + + def attributes_with_index + attributes.select { |a| !a.reference? && a.has_index? } + end + + # Used by the migration template to determine the parent name of the model + def parent_class_name + options[:parent] || "ApplicationRecord" + end + end + end +end diff --git a/activerecord/lib/rails/generators/active_record/model/templates/model.rb.tt b/activerecord/lib/rails/generators/active_record/model/templates/model.rb.tt new file mode 100644 index 0000000000..55dc65c8ad --- /dev/null +++ b/activerecord/lib/rails/generators/active_record/model/templates/model.rb.tt @@ -0,0 +1,13 @@ +<% module_namespacing do -%> +class <%= class_name %> < <%= parent_class_name.classify %> +<% attributes.select(&:reference?).each do |attribute| -%> + belongs_to :<%= attribute.name %><%= ', polymorphic: true' if attribute.polymorphic? %><%= ', required: true' if attribute.required? %> +<% end -%> +<% attributes.select(&:token?).each do |attribute| -%> + has_secure_token<% if attribute.name != "token" %> :<%= attribute.name %><% end %> +<% end -%> +<% if attributes.any?(&:password_digest?) -%> + has_secure_password +<% end -%> +end +<% end -%> diff --git a/activerecord/lib/rails/generators/active_record/model/templates/module.rb.tt b/activerecord/lib/rails/generators/active_record/model/templates/module.rb.tt new file mode 100644 index 0000000000..a3bf1c37b6 --- /dev/null +++ b/activerecord/lib/rails/generators/active_record/model/templates/module.rb.tt @@ -0,0 +1,7 @@ +<% module_namespacing do -%> +module <%= class_path.map(&:camelize).join('::') %> + def self.table_name_prefix + '<%= namespaced? ? namespaced_class_path.join('_') : class_path.join('_') %>_' + end +end +<% end -%> |