class HotGlue::ScaffoldGenerator

Attributes

nest_with[RW]
path[RW]
plural[RW]
singular[RW]
singular_class[RW]

Public Class Methods

new(*meta_args) click to toggle source

def erb_replace_ampersands

if @template_builder.is_a?(HotGlue::ErbTemplate)
  @output_buffer.gsub!('\%', '%')
end

end

Calls superclass method
# File lib/generators/hot_glue/scaffold_generator.rb, line 93
def initialize(*meta_args)
  super

  begin
    @the_object = eval(class_name)
  rescue StandardError => e
    message = "*** Oops: It looks like there is no object for #{class_name}. Please define the object + database table first."
    raise(HotGlue::Error, message)
  end

  if @specs_only && @no_specs
    raise(HotGlue::Error, "*** Oops: You seem to have specified both the --specs-only flag and --no-specs flags. this doesn't make any sense, so I am aborting. sorry.")
  end

  if options['markup'] == "erb"
    @template_builder = HotGlue::ErbTemplate.new
  elsif options['markup'] == "slim"
    puts "SLIM IS NOT IMPLEMENTED; please see https://github.com/jasonfb/hot-glue/issues/3"
    abort
    @template_builder = HotGlue::SlimTemplate.new

  elsif options['markup'] == "haml"
    @template_builder = HotGlue::HamlTemplate.new
  end
  @markup =  options['markup']


  args = meta_args[0]
  @singular = args.first.tableize.singularize # should be in form hello_world
  @plural = options['plural'] || @singular + "s" # supply to override; leave blank to use default
  @auth = options['auth'] || "current_user"
  @auth_identifier = options['auth_identifier'] || (!@auth.nil? && @auth.gsub("current_", "")) || nil


  @nest = (!options['nest'].empty? && options['nest']) || nil
  @namespace = options['namespace'] || nil

  @singular_class = @singular.titleize.gsub(" ", "")
  @exclude_fields = []
  @exclude_fields += options['exclude'].split(",").collect(&:to_sym)

  if !options['include'].empty?
    @include_fields = []
    @include_fields += options['include'].split(",").collect(&:to_sym)
  end


  @show_only = []
  if !options['show_only'].empty?
    @show_only += options['show_only'].split(",").collect(&:to_sym)
  end


  @god = options['god'] || options['gd'] || false
  @specs_only = options['specs_only'] || false

  @no_specs = options['no_specs'] || false
  @no_delete = options['no_delete'] || false

  @no_create = options['no_create'] || false
  @no_paginate = options['no_paginate'] || false
  @big_edit = options['big_edit']

  if @god
    @auth = nil
  end

  # when in self auth, the object is the same as the authenticated object
  if @auth && auth_identifier == @singular
    @self_auth = true
  end

  @nested_args = []
  if !@nest.nil?
    @nested_args = @nest.split("/")
    @nested_args_plural = {}
    @nested_args.each do |a|
      @nested_args_plural[a] = a + "s"
    end
  end

                             # the @object_owner will always be object that will 'own' the object
                             # for new and create

  if @auth && ! @self_auth && @nested_args.none?
    @object_owner_sym = @auth.gsub("current_", "").to_sym
    @object_owner_eval = @auth
  else

    if @nested_args.any?
      @object_owner_sym = @nested_args.last.to_sym
      @object_owner_eval = "@#{@nested_args.last}"
    else
      @object_owner_sym = ""
      @object_owner_eval = ""
    end
  end

  identify_object_owner
  setup_fields
end

Public Instance Methods

all_form_fields() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 594
def all_form_fields
  @template_builder.all_form_fields(
    columns: @columns,
    show_only: @show_only,
    singular_class: singular_class,
    singular: singular
  )
end
all_line_fields() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 603
def all_line_fields
  @template_builder.all_line_fields(
    columns: @columns,
    show_only: @show_only,
    singular_class: singular_class,
    singular: singular
  )
end
all_objects_root() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 470
def all_objects_root
  if @auth
    if @self_auth
      @singular_class + ".where(id: #{@auth}.id)"
    elsif @nested_args.none?
      @auth + ".#{plural}"
    else
      "@" + @nested_args.last + ".#{plural}"
    end
  else
    @singular_class + ".all"
  end
end
all_objects_variable() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 488
def all_objects_variable
  all_objects_root + ".page(params[:page])"
end
all_views() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 563
def all_views
  res =  %w(index edit _form _line _list _show _errors)

  unless @no_create
    res += %w(new _new_form _new_button)
  end

  res
end
any_nested?() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 484
def any_nested?
  @nested_args.any?
end
auth_identifier() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 393
def auth_identifier
  @auth_identifier
end
auth_object() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 492
def auth_object
  @auth
end
columns_spec_with_sample_data() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 329
def columns_spec_with_sample_data
  @columns.map { |c|
    if eval("#{singular_class}.columns_hash['#{c}']").nil?
      byebug
    end
    type = eval("#{singular_class}.columns_hash['#{c}']").type
    random_data = case type
                  when :integer
                    rand(1...1000)
                  when :string
                    FFaker::AnimalUS.common_name
                  when :text
                    FFaker::AnimalUS.common_name
                  when :datetime
                    Time.now + rand(1..5).days
                  end
    c.to_s + ": '" + random_data.to_s + "'"
  }.join(", ")
end
controller_class_name() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 378
def controller_class_name
  res = ""
  res << @namespace.titleize + "::" if @namespace
  res << plural.titleize.gsub(" ", "") + "Controller"
  res
end
controller_descends_from() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 612
def controller_descends_from
  if defined?(@namespace.titlecase + "::BaseController")
    @namespace.titlecase + "::BaseController"
  else
    "ApplicationController"
  end
end
copy_controller_and_spec_files() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 302
def copy_controller_and_spec_files
  @default_colspan = @columns.size

  unless @specs_only
    template "controller.rb.erb", File.join("#{'spec/dummy/' if Rails.env.test?}app/controllers#{namespace_with_dash}", "#{plural}_controller.rb")
    if @namespace
      begin
        eval(controller_descends_from)
        puts "   skipping   base controller #{controller_descends_from}"
      rescue NameError => e
        template "base_controller.rb.erb", File.join("#{'spec/dummy/' if Rails.env.test?}app/controllers#{namespace_with_dash}", "base_controller.rb")
      end
    end
  end

  unless @no_specs
    template "system_spec.rb.erb", File.join("#{'spec/dummy/' if Rails.env.test?}spec/system#{namespace_with_dash}", "#{plural}_behavior_spec.rb")
  end

  template "#{@markup}/_errors.#{@markup}", File.join("#{'spec/dummy/' if Rails.env.test?}app/views#{namespace_with_dash}", "_errors.#{@markup}")
end
copy_view_files() click to toggle source

def erb_replace_ampersands!(filename = nil)

return if filename.nil?
file = File.open(filename, "r")
contents = file.read
file.close

file = File.open(filename, "w")
file.write( contents.gsub('\%', '%'))
file.close

end

# File lib/generators/hot_glue/scaffold_generator.rb, line 516
def copy_view_files
  return if @specs_only
  all_views.each do |view|
    formats.each do |format|
      source_filename = cc_filename_with_extensions("#{@markup}/#{view}", "#{@markup}")
      dest_filename = cc_filename_with_extensions("#{view}", "#{@markup}")
      dest_filepath = File.join("#{'spec/dummy/' if Rails.env.test?}app/views#{namespace_with_dash}",
                                controller_file_path, dest_filename)


      template source_filename, dest_filepath
      gsub_file dest_filepath,  '\%', '%'

    end
  end

  turbo_stream_views.each do |view|
    formats.each do |format|
      source_filename = cc_filename_with_extensions( "#{@markup}/#{view}.turbo_stream.#{@markup}")
      dest_filename = cc_filename_with_extensions("#{view}", "turbo_stream.#{@markup}")
      dest_filepath = File.join("#{'spec/dummy/' if Rails.env.test?}app/views#{namespace_with_dash}",
                                controller_file_path, dest_filename)


      template source_filename, dest_filepath
      gsub_file dest_filepath,  '\%', '%'

    end
  end
end
create_action() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 651
def create_action
  return false if @self_auth
  return !@no_create
end
destroy_action() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 646
def destroy_action
  return false if @self_auth
  return !@no_delete
end
display_class() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 622
def display_class
  me = eval(singular_class)

  @display_class ||=
    if me.column_names.include?("name") || me.instance_methods(false).include?(:name)
      # note that all class object respond_to?(:name) with the name of their own class
      # this one is unique
      "name"
    elsif me.column_names.include?("to_label") || me.instance_methods(false).include?(:to_label)
      "to_label"
    elsif me.column_names.include?("full_name") || me.instance_methods(false).include?(:full_name)
      "full_name"
    elsif me.column_names.include?("display_name") || me.instance_methods(false).include?(:display_name)
      "display_name"
    elsif me.column_names.include?("email") || me.instance_methods(false).include?(:email)
      "email"
    elsif me.column_names.include?("number") || me.instance_methods(false).include?(:number)
      "number"
    else
      exit_message = "*** Oops: Can't find any column to use as the display label on #{singular_class} model . TODO: Please implement just one of: 1) name, 2) to_label, 3) full_name, 4) display_name, 5) email, or 6) number directly on your #{singular_class} model (either as database field or model methods), then RERUN THIS GENERATOR. (If more than one is implemented, the field to use will be chosen based on the rank here, e.g., if name is present it will be used; if not, I will look for a to_label, etc)"
      raise(HotGlue::Error, exit_message)
    end
end
format() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 298
def format
  nil
end
formats() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 294
def formats
  [format]
end
handler() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 581
def handler
  :erb
end
identify_object_owner() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 195
def identify_object_owner
  auth_assoc = @auth && @auth.gsub("current_","")

  if !@object_owner_sym.empty?
    auth_assoc_field = auth_assoc + "_id"
    assoc = eval("#{singular_class}.reflect_on_association(:#{@object_owner_sym})")

    if assoc
      @ownership_field = assoc.name.to_s + "_id"
    elsif !@nest
      exit_message = "*** Oops: It looks like is no association from current_#{@object_owner_sym} to a class called #{@singular_class}. If your user is called something else, pass with flag auth=current_X where X is the model for your users as lowercase. Also, be sure to implement current_X as a method on your controller. (If you really don't want to implement a current_X on your controller and want me to check some other method for your current user, see the section in the docs for auth_identifier.) To make a controller that can read all records, specify with --god."

    else
      if @god

        exit_message= "*** Oops:  god mode could not find the association(?). something is wrong."
      else
        @auth_check = "current_user"
        @nested_args.each do |arg|

          if !@auth_check.method("#{arg}s")
            exit_message= "*** Oops:  your nesting chain does not have a assocation for #{arg}s on #{@auth_check}  something is wrong."
          end
          byebug
          puts ""
        end
      end

      raise(HotGlue::Error, exit_message)
    end
  end
end
line_path_partial() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 421
def line_path_partial
  "#{@namespace+"/" if @namespace}#{plural}/line"
end
list_column_headings() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 324
def list_column_headings
  @template_builder.list_column_headings(columns: @columns)

end
list_path_partial() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 429
def list_path_partial
  "#{@namespace+"/" if @namespace}#{plural}/list"
end
model_has_strings?() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 585
def model_has_strings?
  false
end
model_search_fields() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 590
def model_search_fields # an array of fields we can search on
  []
end
namespace_with_dash() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 547
def namespace_with_dash
  if @namespace
    "/#{@namespace}"
  else
    ""
  end
end
namespace_with_slash() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 656
 def namespace_with_slash
   if @namespace
     "#{@namespace}/"
  else
    ""
  end
end
namespace_with_trailing_dash() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 555
def namespace_with_trailing_dash
  if @namespace
    "#{@namespace}/"
  else
    ""
  end
end
nested_arity_for_path() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 453
def nested_arity_for_path
  [@nested_args[0..-1].collect{|a| "@#{a}"}].join(", ") #metaprgramming into arity for the Rails path helper
end
nested_assignments() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 437
def nested_assignments
  @nested_args.map{|a| "#{a}: @#{a}"}.join(", ") #metaprgramming into Ruby hash
end
nested_assignments_with_leading_comma() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 441
def nested_assignments_with_leading_comma
  if @nested_args.any?
    ", #{nested_assignments}"
  else
    ""
  end
end
nested_objects_arity() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 449
def nested_objects_arity
  @nested_args.map{|a| "@#{a}"}.join(", ")
end
new_path_name() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 433
def new_path_name
  "new_#{@namespace+"_" if @namespace}#{singular}_path"
end
no_devise_installed() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 496
def no_devise_installed
  !Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.group_by{ |g| g.name }['devise']
end
object_parent_mapping_as_argument_for_specs() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 349
def object_parent_mapping_as_argument_for_specs
  if @nested_args.any?
    ", " + @nested_args.last + ": " + @nested_args.last
  elsif @auth
    ", #{@auth_identifier}: #{@auth}"
  end
end
object_scope() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 457
def object_scope
  if @auth
    if @nested_args.none?
      @auth + ".#{plural}"
    else
      "@" + @nested_args.last + ".#{plural}"
    end
  else
    @singular_class
  end
end
objest_nest_factory_setup() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 357
def objest_nest_factory_setup
  res = ""
  if @auth
    last_parent = ", #{@auth_identifier}: #{@auth}"
  end

  @nested_args.each do |arg|
    res << "  let(:#{arg}) {create(:#{arg} #{last_parent} )}\n"
    last_parent = ", #{arg}: #{arg}"
  end
  res
end
objest_nest_params_by_id_for_specs() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 371
def objest_nest_params_by_id_for_specs
  @nested_args.map{|arg|
    "#{arg}_id: #{arg}.id"
  }.join(",\n          ")
end
paginate() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 664
def paginate
  @template_builder.paginate(plural: plural)
end
path_arity() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 413
def path_arity
  res = ""
  if @nested_args.any?
    res << nested_objects_arity + ", "
  end
  res << "@" + singular
end
path_helper_args() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 397
def path_helper_args
  if @nested_args.any?
    [(@nested_args).collect{|a| "@#{a}"} , singular].join(",")
  else
    singular
  end
end
path_helper_plural() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 409
def path_helper_plural
  "#{@namespace+"_" if @namespace}#{(@nested_args.join("_") + "_" if @nested_args.any?)}#{plural}_path"
end
path_helper_singular() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 405
def path_helper_singular
  "#{@namespace+"_" if @namespace}#{(@nested_args.join("_") + "_" if @nested_args.any?)}#{singular}_path"
end
plural_name() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 389
def plural_name
  plural
end
setup_fields() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 229
def setup_fields
  auth_assoc = @auth && @auth.gsub("current_","")

  if !@include_fields
    @exclude_fields.push :id, :created_at, :updated_at, :encrypted_password,
                         :reset_password_token,
                         :reset_password_sent_at, :remember_created_at,
                         :confirmation_token, :confirmed_at,
                         :confirmation_sent_at, :unconfirmed_email

    @exclude_fields.push( (auth_assoc + "_id").to_sym) if ! auth_assoc.nil?
    @exclude_fields.push( @ownership_field.to_sym ) if ! @ownership_field.nil?


    @columns = @the_object.columns.map(&:name).map(&:to_sym).reject{|field| @exclude_fields.include?(field) }

  else
    @columns = @the_object.columns.map(&:name).map(&:to_sym).reject{|field| !@include_fields.include?(field) }
  end

  @columns.each do |col|
    if col.to_s.starts_with?("_")
      @show_only << col
    end

    if @the_object.columns_hash[col.to_s].type == :integer
      if col.to_s.ends_with?("_id")
        # guess the association name label
        assoc_name = col.to_s.gsub("_id","")
        assoc = eval("#{singular_class}.reflect_on_association(:#{assoc_name})")


        begin
          eval(assoc.class_name)
        rescue NameError => e
          exit_message = "*** Oops: The model #{singular_class} is missing an association for #{assoc_name} or the model doesn't exist. TODO: Please implement a model for #{assoc_name.titlecase}; your model #{singular_class.titlecase} should have_many :#{assoc_name}s.  To make a controller that can read all records, specify with --god."
          raise(HotGlue::Error, exit_message)

        end


        if assoc.nil?
          exit_message= "*** Oops. on the #{singular_class} object, there doesn't seem to be an association called '#{assoc_name}'"
          raise(HotGlue::Error,exit_message)
        end

        assoc_class = eval(assoc.class_name)

        name_list = [:name, :to_label, :full_name, :display_name, :email]


        if name_list.collect{ |field|
          assoc_class.column_names.include?(field.to_s) ||  assoc_class.instance_methods.include?(field)
        }.any?
          # do nothing here
        else
          exit_message= "*** Oops: Missing a label for #{assoc.class_name.upcase}. Can't find any column to use as the display label for the #{assoc.name.to_s} association on the #{singular_class} model . TODO: Please implement just one of: 1) name, 2) to_label, 3) full_name, 4) display_name, or 5) email directly on your #{assoc.class_name.upcase} model (either as database field or model methods), then RERUN THIS GENERATOR. (If more than one is implemented, the field to use will be chosen based on the rank here, e.g., if name is present it will be used; if not, I will look for a to_label, etc)"
          raise(HotGlue::Error,exit_message)
        end
      end
    end
  end
end
show_path_partial() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 425
def show_path_partial
  "#{@namespace+"/" if @namespace}#{plural}/show"
end
singular_name() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 385
def singular_name
  @singular
end
turbo_stream_views() click to toggle source
# File lib/generators/hot_glue/scaffold_generator.rb, line 573
def turbo_stream_views
  res = %w(create  edit update)
  unless @no_delete
    res << 'destroy'
  end
  res
end