require 'rails/generators' require 'rails/generators/rails/app/app_generator' require 'json' require 'active_support/core_ext/hash/indifferent_access' module MobileWorkflowCli class AppGenerator < Rails::Generators::AppGenerator hide! class_option :database, type: :string, aliases: "-d", default: "postgresql", desc: "Configure for selected database (options: #{DATABASES.join("/")})" class_option :skip_test, type: :boolean, default: true, desc: "Skip Test Unit" class_option :force, type: :boolean, default: true, desc: "Force overwrite" class_option :version, type: :boolean, aliases: "-v", desc: "Show version number and quit" class_option :help, type: :boolean, aliases: '-h', desc: "Show this help message and quit" class_option :heroku, type: :boolean, aliases: "-H", default: false, desc: "Create Heroku app" def self.banner "mwf [options]" end def finish_template run "spring stop" build :procfiles super after_bundle do open_api_spec = read_openapi_spec generate_models(open_api_spec) setup_db generate_administrate generate_base_api_controller(open_api_spec) generate_controllers_and_routes(open_api_spec) admin_user = 'admin' admin_password = SecureRandom.base64(12) generate_dot_env(admin_user, admin_password) initial_git_commit deploy_heroku(admin_user, admin_password) if options[:heroku] end end protected def get_builder_class MobileWorkflowCli::AppBuilder end # Todo: MBS - move these methods to the builder class # Ideally override RailsBuilder methods private def read_openapi_spec openapi_spec_path = ARGV[1] say "Loading model from OpenAPI Spec: #{openapi_spec_path}" return JSON.parse(File.read(openapi_spec_path)).with_indifferent_access end def generate_administrate generate 'administrate:install' file 'app/assets/config/manifest.js', <<-CODE //= link administrate/application.css //= link administrate/application.js CODE file 'app/controllers/admin/application_controller.rb', <<-CODE module Admin class ApplicationController < Administrate::ApplicationController before_action :authenticate_admin def authenticate_admin self.class.http_basic_authenticate_with(name: ENV["ADMIN_USER"], password: ENV["ADMIN_PASSWORD"]) end end end CODE end def generate_models(openapi_spec) say "Generating models" openapi_spec[:components][:schemas].each_pair do |name, schema| properties_args = schema['properties'].keys.reject{|key| key == 'id'}.collect{|key| "#{key}:#{schema['properties'][key]['type']}" }.join(' ') generate(:model, "#{name.underscore} #{properties_args}") end end def generate_base_api_controller(openapi_spec) file 'app/controllers/api_controller.rb', <<-CODE class ApiController < ActionController::API end CODE end def generate_controllers_and_routes(openapi_spec) say "Generating controllers" controller_names = [] openapi_spec[:paths].each_pair do |url_path, path| controller_name = url_path.split('/').last.pluralize model = path[:get][:responses]["200"][:content]['application/json'][:schema][:items]['$ref'].split('/').last file "app/controllers/#{controller_name}_controller.rb", <<-CODE class #{controller_name.titleize}Controller < ApiController def index render json: #{model}.where(filter_params) end private def filter_params params.permit() # Add any filter properties here end end CODE controller_names << controller_name end controller_names.each {|n| route "resources :#{n}, only: :index" } # Root route route "root to: 'admin/#{controller_names.first}#index'" end def setup_db rails_command "db:drop" rails_command "db:create" rails_command "db:migrate" end def initial_git_commit git add: "." git commit: %Q{ -m 'Initial commit' } end def generate_dot_env(admin_user, admin_password) file '.env', <<-CODE ADMIN_USER=#{admin_user} ADMIN_PASSWORD=#{admin_password} CODE end def deploy_heroku(admin_user, admin_password) heroku_output = `heroku create` `git push heroku master` `heroku config:set ADMIN_USER=#{admin_user} ADMIN_PASSWORD=#{admin_password}` say "Server created: #{heroku_output}" end end end