#!/usr/bin/env ruby ['fileutils', 'getoptlong', 'yaml', 'rubygems/package', 'zlib'].each do |gemfile| require gemfile end DEFAULT_UNIX_SOCKET_FILE = '/tmp/teamster.app.sock' DEFAULT_APP_STATE_FILE = '/srv/my-site/teamster.app.state' CONFIG_FILE = 'conf/teamster.conf' VERSION = File.read(File.dirname(__FILE__) + '/../VERSION') class TeamsterApp class << self def read_options_and_run read_options run_app end def read_options opts = GetoptLong.new( ["--debug", GetoptLong::NO_ARGUMENT ], ["--version", GetoptLong::NO_ARGUMENT ], ["--prod", GetoptLong::NO_ARGUMENT ], ["--help", GetoptLong::NO_ARGUMENT ], ["--overwrite", GetoptLong::NO_ARGUMENT ], ["--create-module", GetoptLong::REQUIRED_ARGUMENT], ["--import-module", GetoptLong::REQUIRED_ARGUMENT], ["--export-module", GetoptLong::REQUIRED_ARGUMENT], ["--socket-file", GetoptLong::REQUIRED_ARGUMENT], ["--state-file", GetoptLong::REQUIRED_ARGUMENT]) @config = {}.tap do |hsh| opts.each do |opt, arg| opt = opt[/--([\w-]+)/, 1].gsub(/-/, "_").to_sym arg ||= true hsh[opt] = arg end end @config end def run_app if @config[:help] quit detailed_usage elsif @config[:version] quit show_version elsif name = @config[:create_module] create_module_for name.downcase elsif name = @config[:import_module] import_module_for name.downcase elsif name = @config[:export_module] export_module_for name.downcase elsif ARGV.size == 1 command = ARGV.first.to_sym send command else quit usage end end private # --{ COMMANDS }-- # def init current_working_folder = Dir.pwd content = File.dirname(__FILE__) + '/../content' puts "Initializing Teamster in current folder." puts "Creating required content..." FileUtils.mkdir_p "conf" create_config FileUtils.cp_r "#{content}/views", current_working_folder FileUtils.cp_r "#{content}/public", current_working_folder create_file "config.ru", "config_ru" FileUtils.mkdir_p "lib/teamster-modules" create_file "lib/teamster-modules.rb", "teamster_modules" puts "Teamster initialized!" puts "- Run \"teamster --create-module \" to create a placeholder module." puts "- Run \"teamster start\" to start teamster!" end def start puts "Starting teamster..." if File.exists?(CONFIG_FILE) if @config[:prod] socket_file = @config[:socket_file] || DEFAULT_UNIX_SOCKET_FILE state_file = @config[:state_file] || DEFAULT_APP_STATE_FILE Thread.new { exec "puma -d -b unix://#{socket_file} -S #{state_file}" } else Thread.new { exec "rackup -p 9292" } end else quit "\nERROR: Teamster not initialized!\nUnable to start Teamster. Please initialize first by running \"teamster init\"." end end def stop puts "Stopping teamster..." state_file = @config[:state_file] || DEFAULT_APP_STATE_FILE if File.exists? state_file Thread.new { exec "pumactl -S #{state_file} stop" } else quit "\nERROR: state file does not exists: #{state_file}\nUnable to stop teamster." exit 1 end end def restart puts "Restarting teamster..." state_file = @config[:state_file] || DEFAULT_APP_STATE_FILE if File.exists? state_file Thread.new { exec "pumactl -S #{state_file} restart" } else quit "\nERROR: state file does not exists: #{state_file}\nUnable to restart teamster." end end # --{ HELPERS }-- # def quit(msg, code = 0) warn msg exit code end def create_config ask_user_for :title, "What is your team name" File.open(CONFIG_FILE, 'w') {|fh| fh.write @config.to_yaml} end def ask_user_for(opt, question) unless @config[opt] @config[opt] = ask_user question end end def ask_user(question) print "#{question}: " STDIN.gets.strip end def create_module_for(name) puts "Creating placeholders for module #{name}...\n" FileUtils.mkdir_p "lib/teamster-modules/#{name}/views" create_file "lib/teamster-modules/#{name}.rb", "module_placeholder_for", name create_file "lib/teamster-modules/#{name}/#{name}_helper.rb", "module_helper_placeholder_for", name create_file "lib/teamster-modules/#{name}/views/#{name}.erb", "view_placeholder_for", name create_file "lib/teamster-modules/#{name}/views/#{name}_summary.erb", "view_summary_placeholder_for", name puts "\nBasic module creation done!" puts "Controller : \"lib/teamster-modules/#{name}.rb\"" puts "Helper : \"lib/teamster-modules/#{name}/#{name}_helper.rb\"" puts "View : \"lib/teamster-modules/#{name}/views/#{name}.erb\"" puts "Summary View : \"lib/teamster-modules/#{name}/views/#{name}_summary.erb\"" end def import_module_for(name) puts "Importing module: #{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 module. Export depends on cli tool \"unzip\"." end else puts "Unable to find file: #{zip_file}" end end def export_module_for(name) puts "Exporting module: #{name}" zip_file = "#{name}.zip" puts "The following files will be zipped:" puts "- lib/teamster-modules/#{name}.rb" puts '- Everything in folder lib/teamster-modules/#{name}/' if `which zip`.length != 0 `zip -r #{zip_file} lib/teamster-modules/#{name}.rb lib/teamster-modules/#{name}/` puts "\nExported to #{zip_file}!" else puts "\nUnable to export module. Export depends on cli tool \"zip\"." end end def create_file(filename, method, *args) case [File.exists?(filename), !!@config[: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 Run web application: teamster start Run web application in production (uses unix socket & state file): teamster start --prod [--socket-file FILE] [--state-file FILE] 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, start, stop, restart Options (standalone): --help Display this detailed help. --version Version of the teamster used. --create-module NAME Creates a stub of a module --import-module FILE << PENDING IMPLEMENTATION >> --export-module FILE << PENDING IMPLEMENTATION >> Options used with \"start\": --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 Relative/absolute path to the UNIX socket file. --state-file FILE Relative/absolute path to the Puma state file. Options used with \"stop\": --socket-file FILE Relative/absolute path to the UNIX socket file. --state-file FILE Relative/absolute path to the Puma state file. Options used with \"restart\": --socket-file FILE Relative/absolute path to the UNIX socket file. --state-file FILE 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)} run Teamster::Core::App CODE end def teamster_modules <<-CODE Dir.glob(File.dirname(__FILE__) + '/teamster-modules/*.rb').each do |mdl| require mdl end CODE end def module_placeholder_for(name) <<-CODE require_relative \"#{name}/#{name}_helper\" \# NOTE: If the namespace is changed, please take care of the \# namespace of the sub-class and helper modules. module Teamster module Modules class #{name.capitalize} < Base \# Stuff that needs to be done before registration with core. has_helpers #{name.capitalize}Helper \# Add modules here (comma separated) if there are helper modules. views_at \"\#\{File.dirname(__FILE__)\}/#{name}/views\" under_development \# Remove this line when development is finished. \# Register this class so it can be used. register self get '/#{name}/?' do erb :#{name} end end end end CODE end def module_helper_placeholder_for(name) <<-CODE module Teamster module Modules module #{name.capitalize}Helper def #{name}_summary? true end def #{name}_summary erb :#{name}_summary end end end end CODE end def view_placeholder_for(name) <<-CODE

PLACEHOLDER FOR #{name.upcase}

Page under construction. Please check back later!

CODE end def view_summary_placeholder_for(name) <<-CODE

Under development right now..

CODE end end end TeamsterApp.read_options_and_run