Top Level Namespace
Defined Under Namespace
Modules: Api, BulletTrain, Scaffolding, SuperScaffoldBase, SuperScaffoldingHelper, TerminalCommands Classes: FieldGenerator, IncomingWebhooksGenerator, JoinModelGenerator, OauthProviderGenerator, SuperScaffoldGenerator
Constant Summary collapse
- FIELD_PARTIALS =
{ address_field: nil, boolean: "boolean", buttons: "string", cloudinary_image: "string", color_picker: "string", date_and_time_field: "datetime", date_field: "date", email_field: "string", emoji_field: "string", file_field: "attachment", image: "attachment", options: "string", password_field: "string", phone_field: "string", super_select: "string", text_area: "text", text_field: "string", number_field: "integer", trix_editor: "text" }
Instance Method Summary collapse
- #check_required_options_for_attributes(scaffolding_type, attributes, child, parent = nil) ⇒ Object
-
#get_untracked_files(status_lines) ⇒ Object
All untracked files begin with a tab (i.e. - “tapp/models/model.rb”).
- #git_status ⇒ Object
- #has_untracked_files?(status_lines) ⇒ Boolean
- #oauth_scaffold_add_line_to_file(file, content, after, options, additional_options = {}) ⇒ Object
- #oauth_scaffold_directory(directory, options) ⇒ Object
-
#oauth_scaffold_file(file, options) ⇒ Object
there is a bunch of stuff duplicate here, but i’m OK with that for now.
- #oauth_transform_string(string, options) ⇒ Object
- #show_usage ⇒ Object
- #standard_protip ⇒ Object
Instance Method Details
#check_required_options_for_attributes(scaffolding_type, attributes, child, parent = nil) ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/scaffolding/script.rb', line 66 def (scaffolding_type, attributes, child, parent = nil) tableized_parent = nil # Ensure the parent attribute name has the proper namespacing for adding as a foreign key. if parent.present? if child.include?("::") && parent.include?("::") child_parts = child.split("::") parent_parts = parent.split("::") child_parts_dup = child_parts.dup parent_parts_dup = parent_parts.dup # Pop off however many spaces match. child_parts_dup.each.with_index do |child_part, idx| if child_part == parent_parts_dup[idx] child_parts.shift parent_parts.shift else tableized_parent = parent_parts.map(&:downcase).join("_") break end end end # In case we're not working with namespaces, just tableize the parent as is. tableized_parent ||= parent.tableize.singularize.tr("/", "_") if parent.present? end generation_command = case scaffolding_type when "crud" "bin/rails generate model #{child} #{tableized_parent}:references" when "crud-field" "" # This is blank so we can create the proper migration name first after we get the attributes. end # Even if there are attributes passed to the scaffolder, # They may already exist in previous migrations, so we # only register ones that need to be generated. # i.e. - *_ids attributes in the join-model scaffolder. attributes_to_generate = [] attributes.each do |attribute| parts = attribute.split(":") name = parts.shift type = parts.join(":") type_without_option = type.gsub(/{.*}/, "") unless Scaffolding.valid_attribute_type?(type) raise "You have entered an invalid attribute type: #{type}. General data types are used when creating new models, but Bullet Train " \ "uses field partials when Super Scaffolding, i.e. - `name:text_field` as opposed to `name:string`. " \ "Please refer to the Field Partial documentation to view which attribute types are available." end # extract any options they passed in with the field. type, = type.scan(/^(.*){(.*)}/).first || type # create a hash of the options. = if .split(",").map do |s| option_name, option_value = s.split("=") [option_name.to_sym, option_value || true] end.to_h else {} end data_type = if type == "image" && cloudinary_enabled? "string" elsif [:multiple] case type when "file" "attachments" else "jsonb" end else FIELD_PARTIALS[type_without_option.to_sym] end if name.match?(/_id$/) || name.match?(/_ids$/) ||= {} unless [:vanilla] name_without_id = if name.match?(/_id$/) name.gsub(/_id$/, "") elsif name.match?(/_ids$/) name.gsub(/_ids$/, "") end [:class_name] ||= name_without_id.classify file_name = Dir.glob("app/models/**/*.rb").find { |model| model.match?(/#{[:class_name].underscore}\.rb/) } || "" # If a model is namespaced, the parent's model file might exist under # `app/models/`, but sometimes these files are modules that resolve # table names by providing a prefix as opposed to an actual ApplicationRecord. # This check ensures that the _id attribute really is a model. is_active_record_class = [:class_name].constantize.ancestors.include?(ActiveRecord::Base) unless File.exist?(file_name) && is_active_record_class puts "" puts "Attributes that end with `_id` or `_ids` trigger awesome, powerful magic in Super Scaffolding. However, because no `#{[:class_name]}` class was found defined in `#{file_name}`, you'll need to specify a `class_name` that exists to let us know what model class is on the other side of the association, like so:".red puts "" puts " bin/super-scaffold #{scaffolding_type} #{child}#{" " + parent if parent.present?} #{name}:#{type}{class_name=#{name.gsub(/_ids?$/, "").classify}}".red puts "" puts "If `#{name}` is just a regular field and isn't backed by an ActiveRecord association, you can skip all this with the `{vanilla}` option, e.g.:".red puts "" puts " bin/super-scaffold #{scaffolding_type} #{child}#{" " + parent if parent.present?} #{name}:#{type}{vanilla}".red puts "" exit end end end # TODO: Is there ever a case that we want this to be a string? data_type = "references" if name.match?(/_id$/) # For join models, we don't want to generate a migration when # running the crud-field scaffolder in the last step, so we skip *_ids. # Addresses belong_to :addressable, so they don't have to be represented in a migration. unless name.match?(/_ids$/) || data_type.nil? generation_command += " #{name_without_id || name}:#{data_type}" attributes_to_generate << name end end # Generate the models/migrations with the attributes passed. if attributes_to_generate.any? case scaffolding_type # "join-model" is not here because the `rails g` command is written inline in its own scaffolder. when "crud" puts "Generating #{child} model with '#{generation_command}'".green when "crud-field" generation_command = "bin/rails generate migration add_#{attributes_to_generate.join("_and_")}_to_#{child.tableize.tr("/", "_")}#{generation_command}" puts "Adding new fields to #{child} with '#{generation_command}'".green end puts "" unless @options["skip-migration-generation"] untracked_files = has_untracked_files?(git_status) ? get_untracked_files(git_status) : [] generation_thread = Thread.new { `#{generation_command}` } generation_thread.join # Wait for the process to finish. newly_untracked_files = has_untracked_files?(git_status) ? get_untracked_files(git_status) : [] if (newly_untracked_files - untracked_files).size.zero? = <<~MESSAGE Since you have already created the #{child} model, Super Scaffolding won't allow you to re-create it. You can either delete the model and try Super Scaffolding again, or add the `--skip-migration-generation` flag to Super Scaffold the classic Bullet Train way. MESSAGE puts "" puts .red exit 1 end end end end |
#get_untracked_files(status_lines) ⇒ Object
All untracked files begin with a tab (i.e. - “tapp/models/model.rb”).
62 63 64 |
# File 'lib/scaffolding/script.rb', line 62 def get_untracked_files(status_lines) `git ls-files --other --exclude-standard`.split("\n") end |
#git_status ⇒ Object
53 54 55 |
# File 'lib/scaffolding/script.rb', line 53 def git_status `git status`.split("\n") end |
#has_untracked_files?(status_lines) ⇒ Boolean
57 58 59 |
# File 'lib/scaffolding/script.rb', line 57 def has_untracked_files?(status_lines) status_lines.include?("Untracked files:") end |
#oauth_scaffold_add_line_to_file(file, content, after, options, additional_options = {}) ⇒ Object
101 102 103 104 105 106 107 |
# File 'lib/scaffolding/oauth_providers.rb', line 101 def oauth_scaffold_add_line_to_file(file, content, after, , = {}) empty_transformer = Scaffolding::Transformer.new("", "") file = oauth_transform_string(file, ) content = oauth_transform_string(content, ) after = oauth_transform_string(after, ) empty_transformer.add_line_to_file(file, content, after, ) end |
#oauth_scaffold_directory(directory, options) ⇒ Object
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# File 'lib/scaffolding/oauth_providers.rb', line 1 def oauth_scaffold_directory(directory, ) transformed_directory_name = oauth_transform_string(directory, ) empty_transformer = Scaffolding::Transformer.new("", "") begin Dir.mkdir(transformed_directory_name) rescue Errno::EEXIST => _ puts "The directory #{transformed_directory_name} already exists, skipping generation.".yellow rescue Errno::ENOENT => _ puts "Proceeding to generate '#{transformed_directory_name}'." end Dir.foreach(empty_transformer.resolve_template_path(directory)) do |file| file = "#{directory}/#{file}" unless File.directory?(empty_transformer.resolve_template_path(file)) oauth_scaffold_file(file, ) end end end |
#oauth_scaffold_file(file, options) ⇒ Object
there is a bunch of stuff duplicate here, but i’m OK with that for now.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/scaffolding/oauth_providers.rb', line 21 def oauth_scaffold_file(file, ) transformed_file_name = oauth_transform_string(file, ) transformed_file_content = [] empty_transformer = Scaffolding::Transformer.new("", "") skipping = false File.open(empty_transformer.resolve_template_path(file)).each_line do |line| if line.include?("# 🚅 skip when scaffolding.") next end if line.include?("# 🚅 skip this section when scaffolding.") skipping = true next end if line.include?("# 🚅 stop any skipping we're doing now.") skipping = false next end if skipping next end # remove lines with 'remove in scaffolded files.' unless line.include?("remove in scaffolded files.") # only transform it if it doesn't have the lock emoji. if line.include?("🔒") # remove any comments that start with a lock. line.gsub!(/\s+?#\s+🔒.*/, "") else line = oauth_transform_string(line, ) end transformed_file_content << line end end transformed_file_content = transformed_file_content.join transformed_directory_name = File.dirname(transformed_file_name) unless File.directory?(transformed_directory_name) FileUtils.mkdir_p(transformed_directory_name) end puts "Writing '#{transformed_file_name}'." unless silence_logs? File.write(transformed_file_name, transformed_file_content) end |
#oauth_transform_string(string, options) ⇒ Object
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/scaffolding/oauth_providers.rb', line 73 def oauth_transform_string(string, ) name = [:our_provider_name] empty_transformer = Scaffolding::Transformer.new("", "") # get these out of the way first. string = string.gsub("stripe_connect: Stripe", empty_transformer.encode_double_replacement_fix([:gems_provider_name] + ": " + name.titleize)) string = string.gsub("ti-money", empty_transformer.encode_double_replacement_fix([:icon])) if [:icon] string = string.gsub("omniauth-stripe-connect", empty_transformer.encode_double_replacement_fix([:omniauth_gem])) string = string.gsub("stripe_connect", empty_transformer.encode_double_replacement_fix([:gems_provider_name])) string = string.gsub("STRIPE_CLIENT_ID", empty_transformer.encode_double_replacement_fix([:api_key])) string = string.gsub("STRIPE_SECRET_KEY", empty_transformer.encode_double_replacement_fix([:api_secret])) # then try for some matches that give us a little more context on what they're looking for. string = string.gsub("stripe-account", empty_transformer.encode_double_replacement_fix(name.underscore.dasherize + "_account")) string = string.gsub("stripe_account", empty_transformer.encode_double_replacement_fix(name.underscore + "_account")) string = string.gsub("StripeAccount", empty_transformer.encode_double_replacement_fix(name + "Account")) string = string.gsub("Stripe Account", empty_transformer.encode_double_replacement_fix(name.titleize + " Account")) string = string.gsub("Stripe account", empty_transformer.encode_double_replacement_fix(name.titleize + " account")) string = string.gsub("with Stripe", empty_transformer.encode_double_replacement_fix("with " + name.titleize)) # finally, just do the simplest string replace. it's possible this can produce weird results. # if they do, try adding more context aware replacements above, e.g. what i did with 'with'. string = string.gsub("stripe", empty_transformer.encode_double_replacement_fix(name.underscore)) string = string.gsub("Stripe", empty_transformer.encode_double_replacement_fix(name)) empty_transformer.decode_double_replacement_fix(string) end |
#show_usage ⇒ Object
220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/scaffolding/script.rb', line 220 def show_usage puts "" puts "🚅 usage: bin/super-scaffold [type] (... | --help | --field-partials)" puts "" puts "Supported types of scaffolding:" puts "" BulletTrain::SuperScaffolding.scaffolders.each do |key, _| puts " #{key}" end puts "" puts "Try `bin/super-scaffold [type]` for usage examples.".blue puts "" end |
#standard_protip ⇒ Object
48 49 50 51 |
# File 'lib/scaffolding/script.rb', line 48 def standard_protip puts "🏆 Protip: Commit your other changes before running Super Scaffolding so it's easy to undo if you (or we) make any mistakes." puts "If you do that, you can reset to your last commit state by using `git checkout .` and `git clean -d -f` ." end |