module Camping module CommandsHelpers # transform app_name to snake case def self.to_snake_case(string) string = string.to_s if string.class == Symbol string.gsub(/::/, '/'). gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). gsub(/([a-z\d])([A-Z])/,'\1_\2'). tr("-", "_"). downcase end # transform app_name to camel Case def self.to_camel_case(string) cammelled = "" to_snake_case(string).split("_").each do |seq| cammelled << seq.capitalize end cammelled end # Helper method that generates an app name from command line input. def self.app_name_from_input(app_name) app_name = :Camp if app_name == nil app_name = app_name.to_sym if app_name.class == String snake_app_name = to_snake_case(app_name) camel_app_name = to_camel_case(snake_app_name) {app_name: camel_app_name.to_sym, snake_name: snake_app_name, camel_name: camel_app_name} end RouteCollection = Struct.new(:routes) class RouteCollection # Displays formatted routes from a route collection # Assumes that Route structs are stored in :routes. def display current_app, current_controller, current_method = "", "", "" puts "App VERB Route" routes.each { |r| if current_app != r.app.to_s current_app = r.app.to_s puts "-----------------------------------" puts r.app_header end if current_controller != r.controller.to_s current_controller = r.controller.to_s puts r.controller_header end puts r.padded_message true } end end # Route Struct, for making and formatting a route. Route = Struct.new(:http_method, :controller, :app, :url) class Route def to_s "#{controller}: #{http_method}: #{url} - #{replace_reg url}" end # pad the controller name to be the right length, if we can. def padded_message(with_method = false) "#{pad}#{(with_method ? http_method.to_s.upcase.ljust(pad.length, " ") : pad)}#{replace_reg url}" end def app_header "#{app.to_s}" end def controller_header "#{pad}#{app.to_s}::#{controller.to_s}" end protected def http_methods ["get", "post", "put", "patch", "delete"] end def replace_reg(pattern = "") xstr = "([^/]+)"; nstr = "(\d+)" pattern = pattern.gsub(xstr, ":string").gsub("(\\d+)", ":integer") unless pattern == "/" pattern end def pad " " end end class RoutesParser def self.parse(app) new(app).parse end def initialize(app = Camping) @parent_app, @routes = app, [] end def parse routes = @parent_app.make_camp collected_routes = [] make_routes = -> (a) { a::X.all.map {|c| k = a::X.const_get(c) im = k.instance_methods(false).map!(&:to_s) methods = im & ["get", "post", "put", "patch", "delete"] if k.respond_to?:urls methods.each { |m| k.urls.each { |u| collected_routes.append Camping::CommandsHelpers::Route.new(m,c,a.to_s,u) } } end } } if @parent_app == Camping @parent_app::Apps.each {|a| make_routes.(a) } else make_routes.(@parent_app) end routes_collection = Camping::CommandsHelpers::RouteCollection.new(collected_routes) end end end class Generators class << self # write a file def write(file, content) raise "Cannot write to nil file." unless file folder = File.dirname(file) `mkdir -p #{folder}` unless File.exist?(folder) File.open(file, 'w') { |f| f.write content } end # read a file def read(file) File.read(file) end def make_camp_file(app_name="Tent") write "camp.rb", <<-RUBY require 'camping' Camping.goes :#{app_name} module #{app_name} module Models end module Controllers class Index def get @title = "#{app_name}" render :index end end end module Helpers end module Views def layout html do head do title '#{app_name}' link :rel => 'stylesheet', :type => 'text/css', :href => '/styles.css', :media => 'screen' end body do h1 '#{app_name}' div.wrapper! do self << yield end end end end def index h2 "Let's go Camping" end end end RUBY end # makes a gitignore. def make_gitignore write '.gitignore', <<-GIT .DS_Store node_modules/ tmp/ db/camping.db db/camping.sqlite3 db/camping.sqlite GIT end def make_ruby_version write '.ruby-version', <<-RUBY #{RUBY_VERSION} RUBY end # writes a rakefile def make_rakefile write 'Rakefile', <<-TXT # Rakefile require 'rake' require 'rake/clean' require 'rake/testtask' require 'tempfile' require 'open3' task :default => :test task :test => 'test:all' namespace 'test' do Rake::TestTask.new('all') do |t| t.libs << 'test' t.test_files = FileList['test/test_*.rb'] end end TXT end # write a config.kdl def make_configkdl write 'config.kdl', <<-KDL // config.kdl hostname "localhost" KDL end # write a Gemfile def make_gemfile write 'Gemfile', <<-GEM # frozen_string_literal: true source 'https://rubygems.org' gem 'camping' gem 'puma' gem 'rake' group :production do gem 'rack-ssl-enforcer' end group :development do end group :test do gem 'minitest', '~> 5.0' gem 'minitest-reporters' gem 'rack-test' gem 'minitest-hooks' end GEM end # write a README.md def make_readme write 'README.md', <<-READ # Camping Camping is really fun and I hope you enjoy it. Start camping by running: `camping` in the root directory. To start Camping in development mode run: `camping -e development READ end def make_public_folder Dir.mkdir("public") unless Dir.exist?("public") end def make_test_folder Dir.mkdir("test") unless Dir.exist?("test") write 'test/test_helper.rb', <<-RUBY $:.unshift File.dirname(__FILE__) + '/../' # shift to act like we're in the regular degular directory begin require 'rubygems' rescue LoadError end require 'camping' require 'minitest/autorun' require 'minitest' require 'rack/test' require "minitest/reporters" Minitest::Reporters.use! [Minitest::Reporters::DefaultReporter.new(:color => true)] class TestCase < Minitest::Test include Rack::Test::Methods def self.inherited(mod) mod.app = Object.const_get(mod.to_s[/\w+/]) super end class << self attr_accessor :app end def body() last_response.body end def app() self.class.app end def assert_reverse begin yield rescue Exception else assert false, "Block didn't fail" end end def assert_body(str) case str when Regexp assert_match(str, last_response.body.strip) else assert_equal(str.to_s, last_response.body.strip) end end def assert_status(code) assert_equal(code, last_response.status) end def test_silly; end end RUBY end end end class Commands class << self # A helper method to spit out Routes for an application def routes(theApp = Camping, silent = false) routes = Camping::CommandsHelpers::RoutesParser.parse theApp routes.display unless silent == true return routes end def new_cmd(app_name=:Camp) # Normalize the app_name Camping::CommandsHelpers.app_name_from_input(app_name) => {app_name:, snake_name:, camel_name:} # make a directory then move there. # _original_dir = Dir.pwd Dir.mkdir("#{snake_name}") unless Dir.exist?("#{snake_name}") Dir.chdir("#{snake_name}") # generate a new camping app in a directory named after it: Generators::make_camp_file(camel_name) Generators::make_gitignore() Generators::make_rakefile() Generators::make_ruby_version() Generators::make_configkdl() Generators::make_gemfile() Generators::make_readme() Generators::make_public_folder() Generators::make_test_folder() # optionally add omnibus support # add src/ folder # add lib/ folder # add views/ folder # optionally add a local database too, through guidebook # add db/ folder # add db/migrate folder # add db/config.kdl # append migrations stuff to Rakefile. `ls` end # TODO: Create this generator # generates the apps folder from apps found in camp.rb or config.ru # def generate_apps_folder # end end end end