# frozen_string_literal: truerequire"active_support/time"require"active_support/core_ext/string/starts_ends_with"moduleRailsmoduleGeneratorsclassGeneratedAttribute# :nodoc:INDEX_OPTIONS=%w(index uniq)UNIQ_INDEX_OPTIONS=%w(uniq)DEFAULT_TYPES=%w(
attachment
attachments
belongs_to
boolean
date
datetime
decimal
digest
float
integer
references
rich_text
string
text
time
timestamp
token
)attr_accessor:name,:typeattr_reader:attr_optionsattr_writer:index_nameclass<<selfdefparse(column_definition)name,type,index_type=column_definition.split(":")# if user provided "name:index" instead of "name:string:index"# type should be set blank so GeneratedAttribute's constructor# could set it to :stringindex_type,type=type,nilifvalid_index_type?(type)type,attr_options=*parse_type_and_options(type)type=type.to_symiftypeifdangerous_name?(name)raiseError,"Could not generate field '#{name}', as it is already defined by Active Record."endiftype&&!valid_type?(type)raiseError,"Could not generate field '#{name}' with unknown type '#{type}'."endifindex_type&&!valid_index_type?(index_type)raiseError,"Could not generate field '#{name}' with unknown index '#{index_type}'."endiftype&&reference?(type)ifUNIQ_INDEX_OPTIONS.include?(index_type)attr_options[:index]={unique: true}endendnew(name,type,index_type,attr_options)enddefdangerous_name?(name)defined?(ActiveRecord::Base)&&ActiveRecord::Base.dangerous_attribute_method?(name)enddefvalid_type?(type)DEFAULT_TYPES.include?(type.to_s)||!defined?(ActiveRecord::Base)||ActiveRecord::Base.lease_connection.valid_type?(type)enddefvalid_index_type?(index_type)INDEX_OPTIONS.include?(index_type.to_s)enddefreference?(type)[:references,:belongs_to].include?typeendprivate# parse possible attribute options like :limit for string/text/binary/integer, :precision/:scale for decimals or :polymorphic for references/belongs_to# when declaring options curly brackets should be useddefparse_type_and_options(type)casetypewhen/(text|binary)\{([a-z]+)\}/parsed_type,parsed_options=$1,{size: $2.to_sym}when/(string|text|binary|integer)\{(\d+)\}/parsed_type,parsed_options=$1,{limit: $2.to_i}when/decimal\{(\d+)[,.-](\d+)\}/parsed_type,parsed_options=:decimal,{precision: $1.to_i,scale: $2.to_i}when/(references|belongs_to)\{(.+)\}/parsed_type=$1provided_options=$2.split(/[,.-]/)parsed_options=Hash[provided_options.map{|opt|[opt.to_sym,true]}]elseparsed_type,parsed_options=type&.remove("!"),{}endiftype&.ends_with?("!")parsed_options[:null]=falseendreturnparsed_type,parsed_optionsendenddefinitialize(name,type=nil,index_type=false,attr_options={})@name=name@type=type||:string@has_index=INDEX_OPTIONS.include?(index_type)@has_uniq_index=UNIQ_INDEX_OPTIONS.include?(index_type)@attr_options=attr_optionsenddeffield_type@field_type||=casetypewhen:integerthen:number_fieldwhen:float,:decimalthen:text_fieldwhen:timethen:time_fieldwhen:datetime,:timestampthen:datetime_fieldwhen:datethen:date_fieldwhen:textthen:textareawhen:rich_textthen:rich_textareawhen:booleanthen:checkboxwhen:attachment,:attachmentsthen:file_fieldelse:text_fieldendenddefdefault@default||=casetypewhen:integerthen1when:floatthen1.5when:decimalthen"9.99"when:datetime,:timestamp,:timethenTime.now.to_fs(:db)when:datethenDate.today.to_fs(:db)when:stringthenname=="type"?"":"MyString"when:textthen"MyText"when:booleanthenfalsewhen:references,:belongs_to,:attachment,:attachments,:rich_textthennilelse""endenddefplural_namename.delete_suffix("_id").pluralizeenddefsingular_namename.delete_suffix("_id").singularizeenddefhuman_namename.humanizeenddefindex_name@index_name||=ifpolymorphic?%w(id type).map{|t|"#{name}_#{t}"}elsecolumn_nameendenddefcolumn_name@column_name||=reference??"#{name}_id":nameenddefforeign_key?name.end_with?("_id")enddefreference?self.class.reference?(type)enddefpolymorphic?attr_options[:polymorphic]enddefrequired?reference?&&Rails.application.config.active_record.belongs_to_required_by_defaultenddefhas_index?@has_indexenddefhas_uniq_index?@has_uniq_indexenddefpassword_digest?name=="password"&&type==:digestenddeftoken?type==:tokenenddefrich_text?type==:rich_textenddefattachment?type==:attachmentenddefattachments?type==:attachmentsenddefvirtual?rich_text?||attachment?||attachments?enddefinject_options(+"").tap{|s|options_for_migration.each{|k,v|s<<", #{k}: #{v.inspect}"}}enddefinject_index_optionshas_uniq_index??", unique: true":""enddefoptions_for_migration@attr_options.dup.tapdo|options|ifrequired?options[:null]=falseendifreference?&&!polymorphic?options[:foreign_key]=trueendendenddefto_sifhas_uniq_index?"#{name}:#{type}#{print_attribute_options}:uniq"elsifhas_index?"#{name}:#{type}#{print_attribute_options}:index"else"#{name}:#{type}#{print_attribute_options}"endendprivatedefprint_attribute_optionsifattr_options.empty?""elsifattr_options[:size]"{#{attr_options[:size]}}"elsifattr_options[:limit]"{#{attr_options[:limit]}}"elsifattr_options[:precision]&&attr_options[:scale]"{#{attr_options[:precision]},#{attr_options[:scale]}}"else"{#{attr_options.keys.join(",")}}"endendendendend