module Teamster module CLI def self.included(base) base.extend ClassMethods end module ClassMethods def quit(msg, code = 0) warn msg exit code end def create_config @config['title'] = ask_user "What is your team name" @config['adapters'] = [] File.open(CONFIG_FILE, 'w') {|fh| fh.write @config.to_yaml} end def create_user puts "Creating default user.." users = [{"name" => "Administrator", "pass" => b64_enc(BCrypt::Password.create("password"))}] File.open("./data/users", "w") do |fh| fh.write({"users" => users}.to_yaml) end FileUtils.chmod 0600, './data/users' end def b64_enc(obj) Base64.strict_encode64(obj) end def ask_user(question) print "#{question}: " STDIN.gets.strip end def update_adapter(name) config = YAML.load_file(CONFIG_FILE) adapter = config.fetch('adapters').find {|e| e.fetch("name") == name} quit "No local adapter found with name: #{name}" unless adapter list_from_fs = Dir.glob("lib/teamster-adapters/#{name.gsub(' ', '_')}/**/*.*") list_from_conf = adapter.fetch('files') list_from_conf.concat(list_from_fs).uniq! File.open(CONFIG_FILE, 'w') {|fh| fh.write config.to_yaml} end def update_new_adapters config = YAML.load_file(CONFIG_FILE) adapters = config.fetch('adapters') current_adapters = Dir.glob("lib/teamster-adapters/*.rb").map do |f| f.split('/').last.gsub('.rb', '').gsub('_', ' ') end current_adapters.each do |name| next if adapters.find {|e| e.fetch("name") == name} adapters << {}.tap do |hsh| hsh["name"] = name file_list = Dir.glob("lib/teamster-adapters/#{name.gsub(' ', '_')}/**/*.*") file_list << "lib/teamster-adapters/#{name.gsub(' ', '_')}.rb" hsh["files"] = file_list end end File.open(CONFIG_FILE, 'w') {|fh| fh.write config.to_yaml} end def update_existing_adapters config = YAML.load_file(CONFIG_FILE) adapters = config.fetch('adapters') current_adapters = Dir.glob("lib/teamster-adapters/*.rb").map do |f| f.split('/').last.gsub('.rb', '').gsub('_', ' ') end current_adapters.each do |name| adapter = adapters.find {|e| e.fetch("name") == name} list_from_fs = Dir.glob("lib/teamster-adapters/#{name.gsub(' ', '_')}/**/*.*") list_from_conf = adapter.fetch('files') list_from_conf.concat(list_from_fs).uniq! end File.open(CONFIG_FILE, 'w') {|fh| fh.write config.to_yaml} end def add_adapter_to_config(name, filename) @config = YAML.load_file CONFIG_FILE adapter = { 'name' => name, 'files' => [ "lib/teamster-adapters/#{filename}.rb", "lib/teamster-adapters/#{filename}/#{filename}_helper.rb", "lib/teamster-adapters/#{filename}/views/#{filename}.erb", "lib/teamster-adapters/#{filename}/views/#{filename}_summary.erb" ] } @config.tap do |hsh| adapters = hsh.fetch('adapters') adapters << adapter end File.open(CONFIG_FILE, 'w') {|fh| fh.write @config.to_yaml} end def create_adapter_for(name) puts "Creating placeholders for adapter #{name}...\n" filename = name.split(' ').map{|a| a.downcase}.join('_') class_name = name.split(' ').map{|a| a.capitalize}.join('') FileUtils.mkdir_p "lib/teamster-adapters/#{filename}/views" create_file "lib/teamster-adapters/#{filename}.rb", "adapter_placeholder_for", filename, class_name create_file "lib/teamster-adapters/#{filename}/#{filename}_helper.rb", "adapter_helper_placeholder_for", filename, class_name create_file "lib/teamster-adapters/#{filename}/views/#{filename}.erb", "view_placeholder_for", filename, class_name create_file "lib/teamster-adapters/#{filename}/views/#{filename}_summary.erb", "view_summary_placeholder_for", filename, class_name puts "\nBasic adapter creation done!" puts "Controller : \"lib/teamster-adapters/#{filename}.rb\"" puts "Helper : \"lib/teamster-adapters/#{filename}/#{filename}_helper.rb\"" puts "View : \"lib/teamster-adapters/#{filename}/views/#{filename}.erb\"" puts "Summary View : \"lib/teamster-adapters/#{filename}/views/#{filename}_summary.erb\"" add_adapter_to_config name, filename end def import_adapter_for(name) puts "Importing adapter: #{name}" zip_file = "#{name}.zip" if File.exists?(zip_file) if `which unzip`.length != 0 `unzip #{zip_file}` puts "\nSuccessfully imported #{name}. Please restart teamster to use." else puts "\nUnable to import adapter. Export depends on cli tool \"unzip\"." end else puts "Unable to find file: #{zip_file}" end end def export_adapter(name, files) zip_file = "#{name.gsub(' ', '_')}.zip" puts "\nExporting adapter #{name} to zip: #{zip_file}\n\n" puts "The following files will be exported into a zip file:" files.each {|f| puts "- #{f}"} puts if `which zip`.length != 0 if system "zip -r #{zip_file} #{files.join(' ')}" quit "\nExport successful!", 0 else quit "\nError occurred when zipping!", 1 end end end def create_file(filename, method, *args) case [File.exists?(filename), !!@opts[:overwrite]] when [true, false] puts "File \"#{filename}\" exists. Run with --overwrite to overwrite file." else puts "Creating file #{filename}..." File.open(filename, "w") {|fh| fh.write(send(method.to_sym, *args))} end end def show_version "Current version of teamster: #{VERSION}" end def usage <<-HELP Initialize application: teamster init Create an adapter: teamster create my amazing adapter Run web application: teamster start Verify by opening browser and navigating to "http://localhost:9292". For more detailed help, please run "teamster --help". HELP end def detailed_usage <<-DETAIL Teamster is a simple and extensible web portal for teams. Current version: #{VERSION} Usage: teamster [COMMAND] [OPTIONS] Commands: init, list, create, delete, export, import, update, start, stop, restart Options (standalone): --help Display this detailed help. --version Version of the teamster used. Command \"init\" : Initializes the web app. Creates required files for web app. Options: --overwrite If app is already initialized, --overwrite will recreate all the base files. Command \"list\" Lists all teamster adapters. Command \"create\" NAME Creates a skeleton of a teamster adapter. Creates a new entry in the config file. Command \"update\" NAME Updates the files for a teamster adapter in the config file. NAME is optional. If NAME is not given, all new and existing adapters are updated. Command \"delete\" NAME Deletes all the files of the adapter based on the config file. Option: --no-update Does not update new and existing adapters in the config files before deletion. Command \"remove\" NAME Same as DELETE above. Command \"export\" NAME Zips all the files listed in the config file for the specified adapter. Take care to scrub all information in config files, etc. Option: --no-update Does not update new and existing adapters in the config files before deletion. Command \"import\" NAME Import is not yet implemented. Command \"start\" : Used to run the teamster web app. Options: --prod Binds a unix socket to be used by a web server (eg. Nginx) and creates a state file that is used by the Puma app server. --socket-file FILE Required with --prod. Relative/absolute path to the UNIX socket file. --state-file FILE Required with --prod. Relative/absolute path to the Puma state file. Command \"stop\" : Used to stop the teamster web app in production mode. Options: --socket-file FILE Required. Relative/absolute path to the UNIX socket file. --state-file FILE Required. Relative/absolute path to the Puma state file. Command \"restart\" : Used to restart the teamster web app in production mode. Options: --socket-file FILE Required. Relative/absolute path to the UNIX socket file. --state-file FILE Required. Relative/absolute path to the Puma state file. DETAIL end def config_ru <<-CODE require 'teamster' Dir.glob("lib/*.rb").each {|file| require File.absolute_path(file)} Dir.mkdir("log") unless Dir.exists?("log") log_file = File.new("log/teamster-#{Time.now.strftime("%Y%m%d-%H%M")}.log", "a+") $stdout.reopen(log_file) $stderr.reopen(log_file) $stdout.sync=true $stderr.sync=true run Teamster::Core::App CODE end def teamster_adapters <<-CODE Dir.glob(File.dirname(__FILE__) + '/teamster-adapters/*.rb').each do |mdl| require mdl end CODE end def adapter_placeholder_for(filename, class_name) <<-CODE require_relative \"#{filename}/#{filename}_helper\" module Teamster module Adapters class #{class_name} < Sinatra::Base \# Class methods that contain Teamster-Adapter specific helpers. include BaseAdapter \# --{ Stuff that needs to be done before registration with core. }-- \# Add adapters here (comma separated) if there are helper adapters. has_helpers #{class_name}Helper \# Location of the views folder views_at \"\#\{File.dirname(__FILE__)\}/#{filename}/views\" under_development \# Remove this line when development is finished. \# ------------------------------------------------------------------ \# Register this class so it can be used. register self, "#{filename}" configure do enable :logging \# To log, use \"logger.info \". Log messages are captured \# in the file specified in config.ru. end get '/#{filename.gsub('_', '-')}/?' do erb :#{filename} end end end end CODE end def adapter_helper_placeholder_for(filename, class_name) <<-CODE module Teamster module Adapters module #{class_name}Helper def #{filename}_summary? true end def #{filename}_summary erb :#{filename}_summary end end end end CODE end def view_placeholder_for(filename, class_name) <<-CODE

PLACEHOLDER FOR #{filename.split('_').map{|a| a.upcase}.join(' ')}

Page under construction. Please check back later!

CODE end def view_summary_placeholder_for(filename, class_name) <<-CODE

Under development right now..

CODE end end end end